为啥 Python 代码使用 len() 函数而不是长度方法?

Posted

技术标签:

【中文标题】为啥 Python 代码使用 len() 函数而不是长度方法?【英文标题】:Why does Python code use len() function instead of a length method?为什么 Python 代码使用 len() 函数而不是长度方法? 【发布时间】:2010-09-19 05:22:41 【问题描述】:

我知道python有一个len()函数用来判断字符串的大小,但是我想知道为什么它不是字符串对象的方法。

更新

好吧,我意识到我大错特错了。 __len__() 实际上是一个字符串对象的方法。在 Python 中使用字符串对象上的 len 函数来查看面向对象的代码似乎很奇怪。此外,将__len__ 视为名称而不只是 len 也很奇怪。

【问题讨论】:

与您的更新相关:Is there any case where len(someObj) does not call someObj's __len__ function? tl;dr 是的,所以请始终使用 len() 而不是 __len__() 【参考方案1】:

Jim 对this question 的回答可能会有所帮助;我在这里复制它。引用 Guido van Rossum 的话:

首先,出于 HCI 的原因,我选择了 len(x) 而不是 x.len()(def __len__() 来得晚得多)。实际上有两个相互交织的原因,都是 HCI:

(a) 对于某些运算,前缀表示法比后缀读起来更好——前缀(和中缀!)运算在数学中有着悠久的传统,它喜欢视觉帮助数学家思考问题的符号。比较我们将 x*(a+b) 之类的公式重写为 x*a + x*b 的容易程度与使用原始 OO 表示法做同样事情的笨拙。

(b) 当我读到 len(x) 的代码时,我知道它在询问某物的长度。这告诉我两件事:结果是一个整数,而参数是某种容器。相反,当我阅读 x.len() 时,我必须已经知道 x 是某种实现接口或继承自具有标准 len() 的类的容器。看看当一个没有实现映射的类有一个 get() 或 keys() 方法,或者不是文件的东西有一个 write() 方法时,我们偶尔会感到困惑。

以另一种方式说同样的话,我将“len”视为内置操作。我不想失去那个。 /…/

【讨论】:

200+ 票表示缺少 str.len() 浪费了很多人来这里阅读为什么“应该”直观地认为字符串没有长度函数:facepalm:无论如何保留内置的 len() 函数,但为什么不支持字符串等基本事物的合理方法......【参考方案2】:

这里的其他答案中缺少一些东西:len 函数检查 __len__ 方法是否返回非负的 intlen 是一个函数这一事实意味着类不能覆盖此行为以避免检查。因此,len(obj) 提供了obj.len() 无法提供的安全级别。

例子:

>>> class A:
...     def __len__(self):
...         return 'foo'
...
>>> len(A())
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    len(A())
TypeError: 'str' object cannot be interpreted as an integer
>>> class B:
...     def __len__(self):
...         return -1
... 
>>> len(B())
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    len(B())
ValueError: __len__() should return >= 0

当然,可以通过将len 函数重新分配为全局变量来“覆盖”它,但是这样做的代码比覆盖类中方法的代码更明显可疑。

【讨论】:

它还检查 int 不大于 sys.maxsize,并且首先检查 obj.__len__ 是否存在。我写了an answer on another question,列出了所有不同的检查。【参考方案3】:

这里有一些很好的答案,所以在我给出自己的答案之前,我想强调一下我在这里读过的一些宝石(不是红宝石双关语)。

Python 不是纯粹的 OOP 语言,它是一种通用的多范式语言,允许程序员使用他们最熟悉的范式和/或最适合其解决方案的范式。 Python 有一流的函数,所以len 实际上是一个对象。另一方面,Ruby 没有一流的功能。所以len 函数对象有它自己的方法,你可以通过运行dir(len) 来检查。

如果您不喜欢这种方式在您自己的代码中的工作方式,那么您可以使用您喜欢的方法重新实现容器(参见下面的示例)。

>>> class List(list):
...     def len(self):
...         return len(self)
...
>>> class Dict(dict):
...     def len(self):
...         return len(self)
...
>>> class Tuple(tuple):
...     def len(self):
...         return len(self)
...
>>> class Set(set):
...     def len(self):
...         return len(self)
...
>>> my_list = List([1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'])
>>> my_dict = Dict('key': 'value', 'site': '***')
>>> my_set = Set(1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F')
>>> my_tuple = Tuple((1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'))
>>> my_containers = Tuple((my_list, my_dict, my_set, my_tuple))
>>>
>>> for container in my_containers:
...     print container.len()
...
15
2
15
15

【讨论】:

【参考方案4】:

Python 是一种实用的编程语言,len() 是函数而不是 strlistdict 等的方法的原因是实用的。

len() 内置函数直接处理内置类型:len() 的 CPython 实现实际上返回了 PyVarObject C struct 中的 ob_size 字段的值,它代表任何可变大小的内置内存中的对象。这比调用方法快得多——不需要进行属性查找。获取集合中的项目数是一种常见的操作,对于strlistarray.array 等基本和多样化的类型必须有效地工作。

但是,为了提高一致性,将len(o) 应用于用户定义的类型时,Python 调用o.__len__() 作为后备。 __len____abs__ 和 Python Data Model 中记录的所有其他特殊方法使创建行为类似于内置对象的对象变得容易,从而实现了我们称之为“Pythonic”的富有表现力和高度一致的 API。

通过实现特殊方法,您的对象可以支持迭代、重载中缀运算符、管理 with 块中的上下文等。您可以将数据模型视为使用 Python 语言本身的一种方式一个可以无缝集成您创建的对象的框架。

Guido van Rossum 引用 this one 支持的第二个原因是 len(s)s.len() 更容易读写。

符号len(s) 与带有前缀符号的一元运算符一致,例如abs(n)len() 的使用频率比 abs() 的使用频率更高,而且它应该同样易于编写。

可能还有一个历史原因:在 Python 之前的 ABC 语言中(并且在其设计中非常有影响力),有一个一元运算符写为#s,意思是len(s)

【讨论】:

我不相信最优论点。虽然方法调用确实可能更慢,但足够先进的解释器或编译器(Python 解释器做的事情比这更高级)可以识别出调用了内置类型的 len 方法并恢复为返回 @987654349 @ 或任何它需要的东西,至少在绝大多数情况下。 @dionyziz:Python 查找属性的算法非常灵活。您有实例属性、类属性、数据描述符、非数据描述符和多重继承。所有这些都可能在像s.len() 这样的呼叫中发挥作用,但不会影响len(s)【参考方案5】:

你也可以说

>> x = 'test'
>> len(x)
4

使用 Python 2.7.3。

【讨论】:

len 函数在之前的答案中已经提到过。那么这个“答案”有什么意义呢?【参考方案6】:

字符串确实有长度方法:__len__()

Python 中的协议是在具有长度的对象上实现此方法并使用内置的 len() 函数,它会为您调用它,类似于您实现 __iter__() 并使用内置函数的方式在可迭代对象上的iter() 函数中(或在幕后为您调用该方法)。

更多信息请参见Emulating container types。

这里有一本关于 Python 协议主题的好书:Python and the Principle of Least Astonishment

【讨论】:

让我吃惊的是使用len 的理由是多么愚蠢。他们认为强迫人们执行.__len__ 比强迫人们执行.len() 更容易。它是同样的东西,一个看起来干净。如果该语言将具有 OOP __len__,那么制作 len(..) 的意义何在 len、str 等可以与 map、reduce 和 filter 等高阶函数一起使用,而无需定义函数或 lambda 来调用方法。并非一切都围绕 OOP,即使在 Python 中也是如此。 此外,通过使用协议,他们可以提供实现事物的替代方式。例如,您可以使用__iter__ 或仅使用__getitem__ 创建一个可迭代对象,而iter(x) 可以使用任何一种方式。您可以使用__bool____len__ 创建一个可用的布尔上下文对象,bool(x) 可以使用任何一种方式。等等。我认为 Armin 在链接的帖子中很好地解释了这一点——但即使他没有,因为一个经常公开与核心开发人员不和的人的外部解释而称 Python 是白痴也不完全公平…… @Evicatos: +1 表示“并非一切都围绕 OOP”,但我会以“尤其是在 Python 中的”结尾,而不是 甚至 . Python(与 Java、Ruby 或 Smalltalk 不同)并不试图成为一种“纯 OOP”语言。它被明确设计为一种“多范式”语言。列表推导不是可迭代的方法。 zip 是***函数,而不是 zip_with 方法。等等。仅仅因为一切都是对象并不意味着成为对象是每件事最重要的事情。 那篇文章写得太糟糕了,我读了之后感到困惑和愤怒。 “例如,在 Python 2.x 中,元组类型不公开任何非特殊方法,但您可以使用它来制作字符串:”整个事情是近乎难以辨认的句子的融合。【参考方案7】:

有一个len方法:

>>> a = 'a string of some length'
>>> a.__len__()
23
>>> a.__len__
<method-wrapper '__len__' of str object at 0x02005650>

【讨论】:

可以,但不能直接使用。 len 验证 __len__ 的输出。请参阅Is there any case where len(someObj) does not call someObj's __len__ function? Dunder 方法通常并不意味着直接调用。【参考方案8】:

没有?

>>> "abc".__len__()
3

【讨论】:

当然可以,但并不意味着直接调用它。请参阅Is there any case where len(someObj) does not call someObj's __len__ function? Dunder 方法通常并不意味着直接调用。【参考方案9】:
met% python -c 'import this' | grep 'only one'
There should be one-- and preferably only one --obvious way to do it.

【讨论】:

使用对象时显而易见的事情当然是方法。 @Peter:我愿意支付 20 美元给任何有照片证据表明他们将您的评论贴在 Guido 背后的人。 50 美元,如果它在他的额头上。 是的,Python 设计者固守教条,但他们自己并不尊重自己的教条。 $ python -c '导入这个' | grep 明显

以上是关于为啥 Python 代码使用 len() 函数而不是长度方法?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Python 中的字典和列表不继承 'len' 函数

为啥我的合并函数给出奇怪的答案?

为啥 len(None) 不返回 0?

python 基础5 初级函数

为啥函数体在结构中编译,而不是在特征中?

为啥 statistics.mean() 这么慢?