Làm thế nào để tôi có được số lượng các yếu tố trong một danh sách?


1938

Hãy xem xét những điều sau đây:

items = []
items.append("apple")
items.append("orange")
items.append("banana")

# FAKE METHOD:
items.amount()  # Should return 3

Làm thế nào để tôi có được số lượng các yếu tố trong danh sách items?


23
Bạn rõ ràng đang yêu cầu số lượng các yếu tố trong danh sách. Nếu một người tìm kiếm đến đây để tìm kích thước của đối tượng trong bộ nhớ, thì đây là câu hỏi & câu trả lời thực tế mà họ đang tìm kiếm: Làm cách nào để xác định kích thước của một đối tượng trong Python?
Aaron Hall

Câu trả lời:


2640

Các len()chức năng có thể được sử dụng với các loại khác nhau bằng Python - cả tích hợp trong các loại và các loại thư viện. Ví dụ:

>>> len([1,2,3])
3

Tài liệu chính thức 2.x có tại đây: Tài liệu chính thức 3.x có tại đây:len()
len()


239

Làm thế nào để có được kích thước của một danh sách?

Để tìm kích thước của danh sách, hãy sử dụng hàm dựng sẵn , len:

items = []
items.append("apple")
items.append("orange")
items.append("banana")

Và bây giờ:

len(items)

trả về 3.

Giải trình

Mọi thứ trong Python là một đối tượng, bao gồm cả danh sách. Tất cả các đối tượng có một tiêu đề của một số loại trong việc thực hiện C.

Danh sách và các đối tượng dựng sẵn tương tự khác có "kích thước" trong Python, đặc biệt, có một thuộc tính được gọi ob_size, trong đó số lượng phần tử trong đối tượng được lưu trữ. Vì vậy, kiểm tra số lượng đối tượng trong một danh sách là rất nhanh.

Nhưng nếu bạn đang kiểm tra xem kích thước danh sách có bằng 0 hay không, thì đừng sử dụng len- thay vào đó, hãy đặt danh sách đó trong bối cảnh boolean - nó được coi là Sai nếu trống, Đúng khác .

Từ các tài liệu

len(s)

Trả về chiều dài (số lượng vật phẩm) của một đối tượng. Đối số có thể là một chuỗi (chẳng hạn như chuỗi, byte, tuple, danh sách hoặc phạm vi) hoặc một bộ sưu tập (chẳng hạn như từ điển, tập hợp hoặc tập hợp đóng băng).

lenđược thực hiện với __len__, từ các tài liệu mô hình dữ liệu :

object.__len__(self)

Được gọi để thực hiện chức năng tích hợp len(). Nếu trả về độ dài của đối tượng, một số nguyên> = 0. Ngoài ra, một đối tượng không xác định phương thức __nonzero__()[trong Python 2 hoặc __bool__()trong Python 3] và __len__()phương thức của nó trả về 0 được coi là sai trong ngữ cảnh Boolean.

Và chúng ta cũng có thể thấy đó __len__là một phương pháp của danh sách:

items.__len__()

trả về 3.

Các loại dựng sẵn bạn có thể lấy len(độ dài) của

Và trên thực tế, chúng tôi thấy chúng tôi có thể nhận được thông tin này cho tất cả các loại được mô tả:

>>> all(hasattr(cls, '__len__') for cls in (str, bytes, tuple, list, 
                                            xrange, dict, set, frozenset))
True

Không sử dụng lenđể kiểm tra danh sách trống hoặc không trống

Tất nhiên, để kiểm tra độ dài cụ thể, chỉ cần kiểm tra sự bằng nhau:

if len(items) == required_length:
    ...

Nhưng có một trường hợp đặc biệt để kiểm tra danh sách độ dài bằng không hoặc nghịch đảo. Trong trường hợp đó, không kiểm tra sự bình đẳng.

Ngoài ra, không làm:

if len(items): 
    ...

Thay vào đó, chỉ cần làm:

if items:     # Then we have some items, not empty!
    ...

hoặc là

if not items: # Then we have an empty list!
    ...

Tôi giải thích tại sao ở đây nhưng trong ngắn hạn, if itemshoặc if not itemslà cả dễ đọc hơn và hiệu quả hơn.


75

Mặc dù điều này có thể không hữu ích do thực tế là nó có ý nghĩa hơn nhiều so với chức năng "ngoài luồng", một cách hack khá đơn giản sẽ là xây dựng một lớp với một thuộc lengthtính:

class slist(list):
    @property
    def length(self):
        return len(self)

Bạn có thể sử dụng nó như vậy:

>>> l = slist(range(10))
>>> l.length
10
>>> print l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Về cơ bản, nó hoàn toàn giống hệt với một đối tượng danh sách, với lợi ích bổ sung là có một lengthtài sản thân thiện với OOP .

Như mọi khi, phụ cấp của bạn có thể khác nhau.


19
chỉ để bạn biết, bạn có thể chỉ cần thực hiện length = property(len)và bỏ qua chức năng trình bao bọc một dòng và giữ tài liệu / hướng nội lenvới tài sản của bạn.
Tadhg McDonald-Jensen

17

Ngoài ra, lenbạn cũng có thể sử dụng operator.length_hint(yêu cầu Python 3,4+). Đối với listcả hai bình thường đều tương đương, nhưng length_hintlàm cho nó có thể có được độ dài của trình lặp danh sách, có thể hữu ích trong một số trường hợp nhất định:

>>> from operator import length_hint
>>> l = ["apple", "orange", "banana"]
>>> len(l)
3
>>> length_hint(l)
3

>>> list_iterator = iter(l)
>>> len(list_iterator)
TypeError: object of type 'list_iterator' has no len()
>>> length_hint(list_iterator)
3

Nhưng length_hinttheo định nghĩa chỉ là một "gợi ý", vì vậy hầu hết thời gian lenlà tốt hơn.

Tôi đã thấy một số câu trả lời đề nghị truy cập __len__. Điều này hoàn toàn đúng khi xử lý các lớp tích hợp như list, nhưng nó có thể dẫn đến các vấn đề với các lớp tùy chỉnh, bởi vì len(và length_hint) thực hiện một số kiểm tra an toàn. Ví dụ: cả hai đều không cho phép độ dài âm hoặc độ dài vượt quá một giá trị nhất định (sys.maxsize giá trị). Vì vậy, nó luôn an toàn hơn khi sử dụng lenhàm thay vì __len__phương thức!


8

Trả lời câu hỏi của bạn như các ví dụ cũng được đưa ra trước đây:

items = []
items.append("apple")
items.append("orange")
items.append("banana")

print items.__len__()

15
Trong Python, các tên bắt đầu bằng dấu gạch dưới là các phương thức không công khai về mặt ngữ nghĩa và không nên được sử dụng bởi người dùng.
Aaron Hall

2
1 . __foo__: đây chỉ là một quy ước, một cách để hệ thống Python sử dụng các tên không xung đột với tên người dùng. 2 . _foo: đây chỉ là một quy ước, một cách để lập trình viên chỉ ra rằng biến đó là riêng tư (bất cứ điều gì có nghĩa là trong Python). 3 . __foo: điều này có ý nghĩa thực sự: trình thông dịch thay thế tên này _classname__foonhư một cách để đảm bảo rằng tên đó sẽ không trùng với tên tương tự trong một lớp khác. * Không có hình thức gạch dưới nào khác có ý nghĩa trong thế giới Python. * Không có sự khác biệt giữa lớp, biến, toàn cầu, vv trong các quy ước này.
Shai Alon

4
Câu hỏi và trả lời này giải thích lý do tại sao bạn không nên sử dụng các phương thức đặc biệt trực tiếp với tư cách là người dùng: stackoverflow.com/q/40272161/541136
Aaron Hall

@AaronHall nhưng đối với chức năng len thì nó gần như giống nhau. Nó có thể nhanh hơn cho các biến rất lớn. Tuy nhiên, tôi nhận được quan điểm của bạn và chúng ta nên sử dụng len (obj) chứ không phải obj .__ len __ ().
Shai Alon

7

Và để hoàn thiện (chủ yếu là giáo dục), có thể mà không cần sử dụng len()chức năng. Tôi sẽ không chấp nhận điều này như là một lựa chọn tốt ĐỪNG CHƯƠNG TRÌNH THÍCH NÀY TRONG PYTHON , nhưng nó phục vụ mục đích học thuật toán.

def count(list):
    item_count = 0
    for item in list[:]:
        item_count += 1
    return item_count

count([1,2,3,4,5])

(Dấu hai chấm list[:]là ẩn và do đó cũng là tùy chọn.)

Bài học ở đây cho các lập trình viên mới là: Bạn không thể có được số lượng mục trong danh sách mà không đếm chúng tại một số điểm. Câu hỏi trở thành: khi nào là thời điểm tốt để đếm chúng? Ví dụ, mã hiệu suất cao như cuộc gọi hệ thống kết nối cho các ổ cắm (được viết bằng C) connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);, không tính toán độ dài của các phần tử (giao trách nhiệm đó cho mã gọi). Lưu ý rằng độ dài của địa chỉ được truyền qua để lưu bước đếm chiều dài trước? Một tùy chọn khác: tính toán, có thể có ý nghĩa để theo dõi số lượng vật phẩm khi bạn thêm chúng vào trong đối tượng mà bạn vượt qua. Lưu ý rằng điều này chiếm nhiều không gian trong bộ nhớ. Xem câu trả lời của Naftuli Kay .

Ví dụ về việc theo dõi độ dài để cải thiện hiệu suất trong khi chiếm nhiều không gian hơn trong bộ nhớ. Lưu ý rằng tôi không bao giờ sử dụng hàm len () vì độ dài được theo dõi:

class MyList(object):
    def __init__(self):
        self._data = []
        self.length = 0 # length tracker that takes up memory but makes length op O(1) time


        # the implicit iterator in a list class
    def __iter__(self):
        for elem in self._data:
            yield elem

    def add(self, elem):
        self._data.append(elem)
        self.length += 1

    def remove(self, elem):
        self._data.remove(elem)
        self.length -= 1

mylist = MyList()
mylist.add(1)
mylist.add(2)
mylist.add(3)
print(mylist.length) # 3
mylist.remove(3)
print(mylist.length) # 2

Tại sao for item in list[:]:? Tại sao không for item in list:? Ngoài ra, tôi sẽ sử dụng += 1để tăng lên.
Granny đạt được

@GrannyAching Tôi đã đề cập rõ ràng đến dấu hai chấm tùy chọn (bộ xác định phạm vi). Tôi đã để lại công cụ xác định phạm vi trong đó cho mục đích giáo dục - thật có ích khi biết nó được ngụ ý. Một loại danh sách [] cũng được suy ra, như bạn đề xuất - tương đương với mã của tôi. Toán tử gia tăng cũng tương đương với việc thêm 1 vào một biến hiện có, nhưng ngắn hơn trong mọi trường hợp. Vì vậy, tôi đồng ý rằng nó nên được sử dụng nếu đó là lý do của bạn. Mã này không nên được đưa vào sản xuất ở bất cứ đâu, trừ khi học lập trình).
Jonathan Komar

0

Về cách thức len()thực sự hoạt động, đây là triển khai C của nó :

static PyObject *
builtin_len(PyObject *module, PyObject *obj)
/*[clinic end generated code: output=fa7a270d314dfb6c input=bc55598da9e9c9b5]*/
{
    Py_ssize_t res;

    res = PyObject_Size(obj);
    if (res < 0) {
        assert(PyErr_Occurred());
        return NULL;
    }
    return PyLong_FromSsize_t(res);
}

Py_ssize_tlà chiều dài tối đa mà đối tượng có thể có. PyObject_Size()là một hàm trả về kích thước của một đối tượng. Nếu nó không thể xác định kích thước của một đối tượng, nó sẽ trả về -1. Trong trường hợp đó, khối mã này sẽ được thực thi:

if (res < 0) {
        assert(PyErr_Occurred());
        return NULL;
    }

Và một ngoại lệ được đưa ra như là kết quả. Nếu không, khối mã này sẽ được thực thi:

return PyLong_FromSsize_t(res);

resđó là một Csố nguyên, được chuyển đổi thành một con trăn longvà được trả lại. Tất cả các số nguyên python được lưu trữ longskể từ Python 3.


3
Tại sao biết, hoặc biết về vấn đề thực hiện C?
cs95

Vì câu hỏi này không dành riêng cho CPython, câu trả lời này có thể gây hiểu nhầm. PyPy, IronPython, ... có thể và thực hiện nó theo cách khác.
MSeifert
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.