如何在Python中获取列表的元素数量(列表长度)?

2259

如何获取列表items中元素的数量?

items = ["apple", "orange", "banana"]

# There are 3 items.

33
显然,您正在询问列表中元素的数量。如果搜索者想知道内存中对象的大小,这是他们正在寻找的实际问题和答案:如何确定Python中对象的大小? - Russia Must Remove Putin
2
@RussiaMustRemovePutin 这个问题的标题随后被编辑过,因此它似乎不太可能以现在的形式出现在这里。 - Karl Knechtel
11个回答

2933

在Python中,len()函数可用于多种不同的类型 - 包括内置类型和库类型。例如:

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

305
如何获取列表的长度? 要查找列表中元素的数量,请使用内置函数 len
items = []
items.append("apple")
items.append("orange")
items.append("banana")

现在:

len(items)

返回3。

解释

Python中的所有东西都是对象,包括列表。所有对象在C实现中都有某种类型的头文件。

列表和其他类似的内置对象在Python中具有“大小”,特别是具有称为ob_size的属性,其中缓存了对象中元素的数量。因此,检查列表中的对象数量非常快。

但是,如果您要检查列表大小是否为零,请不要使用len - 相反,将列表放入布尔上下文中 - 如果为空,则将其视为False;如果非空,则将其视为True

来自文档

len(s)

返回对象的长度(项目数)。参数可以是序列(例如字符串、字节、元组、列表或范围)或集合(例如字典、集合或冻结集合)。

len 是使用数据模型中的 __len__ 实现的,具体请参考 docs

object.__len__(self)

被调用以实现内置函数len()。 应返回对象的长度,一个整数>= 0。 另外,在布尔上下文中,如果一个对象没有定义__nonzero__()[在Python 2中]或者__bool__()[在Python 3中]方法,并且其__len__()方法返回零,则被视为false。
我们还可以看到__len__是列表的一个方法:
items.__len__()

返回3。

您可以获取长度(len)的内置类型

实际上,我们可以看到我们可以获取所有描述的类型的此信息:

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

不要使用len来测试空列表或非空列表

当然,要测试特定长度,只需测试相等性:

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

但是,对于测试零长度列表或其相反情况有一个特殊的情况。在这种情况下,不要测试相等性。

此外,不要执行以下操作:

if len(items): 
    ...

相反,只需执行:

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

或者

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

在这里解释原因,但简而言之,if itemsif not items比其他替代方案更易读且性能更好。


79

虽然这可能并不实用,因为它更适合作为“开箱即用”的功能,但是一个相当简单的hack是构建一个具有length属性的类:

class slist(list):
    @property
    def length(self):
        return len(self)
你可以像这样使用它:
>>> l = slist(range(10))
>>> l.length
10
>>> print l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

本质上,它与列表对象完全相同,具有面向对象编程友好的length属性的额外好处。

一如既往,您的结果可能会有所不同。


27
你需要知道的是,你可以直接使用length = property(len)来代替一行包装函数,这样做可以保留len函数的文档和内省功能,并使代码更简洁易懂。 - Tadhg McDonald-Jensen

28

除了使用len函数外,您还可以使用operator.length_hint(需要Python 3.4+)。

对于普通的list来说,这两个函数是等效的,但在某些情况下,length_hint可以获得列表迭代器的长度,这可能非常有用:

>>> 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

但是,按照定义,length_hint只是一个“提示”,因此大多数情况下,len更好。

我看到有几个答案建议访问__len__。这在处理像list这样的内置类时是可以的,但是对于自定义类,可能会出现问题,因为len(和length_hint)实现了一些安全检查。例如,两者都不允许负长度或超过某个值(sys.maxsize值)的长度。因此,与其使用__len__方法,总是更安全的做法是使用len函数!


14

为了完整起见(主要是教育目的),可以不使用len()函数来实现。我不认为这是一个好选择,不要用Python编写这样的程序,但对于学习算法有一定的作用。

def count(list):   # list is an iterable object but no type checking here!
    item_count = 0
    for item in list:
        item_count += 1
    return item_count

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

(列表对象必须是可迭代的,这由for..in语句暗示。)

新程序员需要了解的教训是:你无法在不计数的情况下获取列表中的项目数量。问题是:什么时候计数最好?例如,像C语言中编写的套接字的connect系统调用 connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 这样的高性能代码并不计算元素的长度(将这个责任交给调用代码)。请注意,地址的长度被传递以节省首先计算长度的步骤。另一个选项:从计算的角度来看,在你传递的对象内添加项目时跟踪它们的数量可能是有意义的。但要注意,这会占用更多的内存空间。参见Naftuli Kay的答案

以下是跟踪长度以提高性能并占用更多内存空间的示例。请注意,我从未使用过len()函数,因为长度被跟踪:

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

2
为什么要使用 for item in list[:]: 而不是 for item in list:?此外,我会使用 += 1 进行递增。 - Granny Aching
4
如果我们要深入探讨“不建议这样做但很有趣”的话题,我提供 length = max(enumerate(list))[0] + 1 - Kirk Strauser
2
@KirkStrauser 哈哈,我为这个聪明地避免使用 len() 而笑了。 - Jonathan Komar
1
@JonathanKomar:在list[:]中的[:]不是“范围指定符”,而是切片,它会浅复制整个list,没有任何理由。如果省略了[:],它就不会执行任何这样浪费的复制;知道它被隐含了并没有什么好处,因为它根本就没有被隐含。我不知道“列表类型[]也被推断出来”是什么意思,但没有推断类型;对象有类型,任何满足你所做的鸭子类型要求的东西都将被接受(无论是listtuplestr等)。 - ShadowRanger
@ShadowRanger,正如强调的那样,“不要在Python中编写这样的程序[...]学习算法的目的”,你不应该分析这个答案的性能。我很高兴你理解了正在发生的事情。在这种情况下,我会调整答案。 - Jonathan Komar

10

回答您的问题,如之前给出的示例:

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

print items.__len__()

20
在Python中,以下划线开头的名称在语义上被视为非公共方法,不应由用户使用。 - Russia Must Remove Putin
3
  1. __foo__:这只是一种约定,Python 系统使用这种方式来使用名称,以避免与用户名称冲突。
  2. _foo:这只是一种约定,程序员用来表示变量是私有的(无论在 Python 中意味着什么)。
  3. __foo:这具有实际意义:解释器将此名称替换为 _classname__foo,以确保该名称不会与另一个类中的类似名称重叠。
  • 在 Python 世界中,没有其他形式的下划线具有意义。
  • 在这些约定中,类、变量、全局等没有区别。
- Shai Alon
6
本问题解释了为什么用户不应该直接使用特殊方法:https://dev59.com/5VkS5IYBdhLWcg3wKzqq - Russia Must Remove Putin
@AaronHall 但对于len函数来说,它几乎是相同的。对于非常大的变量,它可能会更快。 不过,我理解你的观点,我们应该使用len(obj)而不是obj.len()。 - Shai Alon

6
你可以使用 len() 函数在Python中找到可迭代对象的长度。
my_list = [1, 2, 3, 4, 5]
print(len(my_list))  # OUTPUT: 5

len()函数也适用于字符串:

my_string = "hello"
print(len(my_string))  # OUTPUT: 5

所以,总之,len() 可以与任何序列或集合(或定义了__len__的任何大小对象)一起使用。

5

你可以通过三种方法来查找列表中元素的长度。我将在此处使用性能分析比较这3种方法。

方法1:使用 len()

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

print(len(items))

输出:

3

方法二:使用朴素计数方法

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

counter = 0
for i in items:
    counter = counter + 1

print(counter)

输出:

3

方法三:使用 length_hint()

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

from operator import length_hint
list_len_hint = length_hint(items)
print(list_len_hint)

输出:

3

性能分析 - Naive vs len() vs length_hint()

注意:为了进行比较,我将输入列表更改为一个大的集合,以便比较这些方法的时间差异。

items = list(range(100000000))

# Performance Analysis
from operator import length_hint
import time

# Finding length of list
# using loop
# Initializing counter

start_time_naive = time.time()
counter = 0
for i in items:
    # incrementing counter
    counter = counter + 1
end_time_naive = str(time.time() - start_time_naive)

# Finding length of list
# using len()
start_time_len = time.time()
list_len = len(items)
end_time_len = str(time.time() - start_time_len)

# Finding length of list
# using length_hint()
start_time_hint = time.time()
list_len_hint = length_hint(items)
end_time_hint = str(time.time() - start_time_hint)

# Printing Times of each
print("Time taken using naive method is : " + end_time_naive)
print("Time taken using len() is : " + end_time_len)
print("Time taken using length_hint() is : " + end_time_hint)

输出:

Time taken using naive method is : 7.536813735961914
Time taken using len() is : 0.0
Time taken using length_hint() is : 0.0

结论

可以清楚地看到,与其他两种方法相比,naive所需的时间非常长,因此len()length_hint()是最好的选择。


1
这是糟糕的微基准测试代码。仅使用time.time()计时一次执行?学习使用timeit模块;有如此多的混淆变量,以至于除了“循环和计数较慢”之外,无法从您的测试中得出任何结论。 - ShadowRanger

5
在Python中有一个名为len()的内置函数,可以在这些情况下帮助您。
>>> a = [1,2,3,4,5,6]
>>> len(a)  # Here the len() function counts the number of items in the list.
6

对于字符串来说,情况会略有不同:它计算的是字符数。

>>> a = "Hello"
>>> len(a)
5

说它“计数”可能会误导,因为在大多数情况下,它只是检索已在对象上定义的整数,而不是像“计数”所暗示的那样迭代并递增一个数字。 - wjandrea

4
为了获取Python中所有连续对象的元素数量,您需要使用 len() 方法,例如:
a = range(1000) # range
b = 'abcdefghijklmnopqrstuvwxyz' # string
c = [10, 20, 30] # List
d = (30, 40, 50, 60, 70) # tuple
e = {11, 21, 31, 41} # set

len()方法可以用于所有上述数据类型,因为它们都是可迭代的,即您可以对它们进行迭代。

all_var = [a, b, c, d, e] # All variables are stored to a list
for var in all_var:
    print(len(var))
len() 方法的粗略估计
def len(iterable, /):
    total = 0
    for i in iterable:
        total += 1
    return total

“任何可迭代对象”——这是不正确的。我认为你的意思是“任何序列”。例如,生成器:len(x for x in range(8)) -> TypeError: object of type 'generator' has no len() - wjandrea
遍历器(iterator)和可迭代对象(iterable)之间有一些微小的差别。所有的遍历器都是可迭代对象,但不是反过来。请参考此文章以获取更多解释 https://www.geeksforgeeks.org/python-difference-iterable-iterator/amp/ - Comsavvy
如果我们要挑剔的话,它适用于任何集合。在setdict上运行得非常好(两者都不是序列)。 - ShadowRanger
@ShadowRanger 没错,说得好。不过文档中说“参数可以是序列集合”。另一方面,如果我们遵循collections.abc,那么任何Sequence都是根据定义一个Collection,但我们只需要一个Sized对象来获取其长度。因此,我认为为了这个答案的缘故,最好说“任何对象”,避免过于复杂化事情。 - wjandrea
经过更正,这个答案在可迭代对象方面仍然是不正确的。len() 方法适用于那些数据类型,并不是因为它们是可迭代的,而是因为它们有一个 __len__() 方法,也就是说它们有一个预先知道的大小,在迭代之前就已经确定了。而且我不确定当它实际上在任何(有限的)可迭代对象上工作时,那个粗略的估计有多有用。 - wjandrea

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接