在Python中,使用arr.__len__()来获取数组长度是否是首选方法?

758

Python中,以下是获取元素数量的唯一方法吗?

arr.__len__()

如果是这样,为什么会有奇怪的语法呢?

我在为什么Python的len函数比__len__方法更快?中添加了一个解释。 - Vlad Bezden
8个回答

1257
my_list = [1,2,3,4,5]
len(my_list)
# 5

对于元组也是一样的:

my_tuple = (1,2,3,4,5)
len(my_tuple)
# 5

还有字符串,它们实际上只是字符数组:

my_string = 'hello world'
len(my_string)
# 11

它是有意这样做的,这样列表、元组和其他容器类型或可迭代对象就不需要显式实现公共的.length()方法,而是可以检查实现了“魔法”__len__()方法的任何内容的len()

当然,这可能看起来是多余的,但长度检查实现可能在同一语言内差别很大。一个集合类型使用.length()方法,另一种类型使用.length属性,而另一种类型则使用.count()。具有语言级关键字统一了所有这些类型的入口点。因此,即使您认为不是元素列表的对象也可以进行长度检查。这包括字符串、队列、树等。

len()的函数特性也非常适合函数式编程风格。

lengths = map(len, list_of_containers)

36
len()是一个全局内置函数;len()是对象可以实现的方法。len(foo)通常会调用foo.len()。 - Tim Lesher
1
@BT:我怀疑 __len__.length() 之间的部分原因是以双下划线(dunders)开头和结尾的所有名称都:1)保留了语言使用,2)允许语言绕过正常查找规则(例如,该方法可以直接在实例的类上查找,而不必检查实例是否使用相同名称的属性遮盖了它)。 CPython 参考解释器利用第二点来加速特殊方法的隐式调用(无需经过 __getattribute____getattr__,无需检查实例 dict 等等)。 - ShadowRanger
在编译语言中,这并不那么重要,但在已经很慢、高度动态的解释语言中,将频繁使用的核心概念定义为更易于优化的方式是很重要的。长度的概念也与真伪值相关,因此当你执行if seq:时会涉及到它;CPython 可以通过 C 结构体中的一对指针优化到 C 查找,接着如果未定义它,则查找 tp_len,调用它以获取一个原始的 C 有符号 size_t,并检查结果是否为 0 或非 0 - ShadowRanger
使用 .length() 会将一个非保留名称保留为长度,并对表面上与其他任何名称没有区别的名称应用优化,或者不执行这样的保留和优化,需要显式地 if seq.length():,并涉及多个 dict 查找、绑定方法构造和 Python 级别 int 的创建(而不仅仅是 C 级别的有符号 size_t),即使是内置函数也会使一个简单的“它是空的还是非空的?”测试变得更加缓慢(一个快速测试将其调用 length 方法作为测试的一部分的时间放在了超过 4 倍,而不仅仅是 if seq:)。 - ShadowRanger

54

如果你想获取任何可以计算长度的东西(如列表,字典,元组,字符串等)的长度,你可以调用它的 len 方法。

l = [1,2,3,4]
s = 'abcde'
len(l) #returns 4
len(s) #returns 5
"奇怪"语法的原因是Python在内部将len(object)转换为object.__len__()。这适用于任何对象。因此,如果你定义了某个类,并且它有长度属性,则只需在其上定义一个__len__()方法,然后就可以在实例上调用len了。"

26

直接使用 len(arr)

>>> import array
>>> arr = array.array('i')
>>> arr.append('2')
>>> arr.__len__()
1
>>> len(arr)
1

24

获取任何Python对象长度的首选方法是将其作为参数传递给len函数。在内部,Python将尝试调用传递的对象的特殊__len__方法。


24

Python使用鸭子类型: 只要对象在当前情况下具有适当的接口,它就不关心该对象是什么。 当您对对象调用内置函数len()时,实际上是在调用其内部__len__方法。 自定义对象可以实现这个接口,而len()将返回答案,即使对象在概念上不是一个序列。

完整接口列表请参见此处:http://docs.python.org/reference/datamodel.html#basic-customization


8

你可以像前面的答案建议的那样使用len(arr)来获取数组的长度。如果你想要一个二维数组的尺寸,你可以使用arr.shape返回高度和宽度。


7

len(list_name)函数以列表为参数,并调用列表的__len__()函数。


3
Python建议用户使用len()而不是__len__(),以保持一致性,正如其他人所说。但是,还有其他好处:
对于一些内置类型,例如liststrbytearray等,len()的Cython实现采用了一种快捷方式。它直接返回C结构中的ob_size,这比调用__len__()更快。
如果您对这些细节感兴趣,可以阅读Luciano Ramalho的书籍《流畅的Python》。其中有许多有趣的细节,可以帮助您更深入地了解Python。

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