为什么 Python 代码使用 len() 函数而不是 length 方法?
- 2025-02-17 09:25:00
- admin 原创
- 77
问题描述:
我知道python有一个len()
用于确定字符串大小的函数,但我想知道为什么它不是字符串对象的方法?
解决方案 1:
字符串确实有一个长度方法:__len__()
Python 中的协议是在具有长度的对象上实现此方法并使用内置len()
函数为您调用它,类似于您在可迭代的对象上实现__iter__()
和使用内置iter()
函数(或在后台为您调用该方法)的方式。
有关详细信息,请参阅模拟容器类型。
这是一篇关于 Python 协议主题的好文章:Python 和最小惊讶原则
解决方案 2:
Jim 对这个问题的回答可能会有所帮助;我把它复制到这里。引用 Guido van Rossum 的话:
首先,我选择 len(x) 而不是 x.len() 是出于 HCI 的原因(def __len__() 出现得晚得多)。实际上有两个相互交织的原因,都是 HCI:
(a) 对于某些运算,前缀表示法比后缀表示法读起来更好 — 前缀(和中缀!)运算在数学中有着悠久的传统,数学家喜欢用视觉符号来帮助数学家思考问题。比较一下,我们将 x(a+b) 这样的公式重写为 xa + x*b 的简单性与使用原始 OO 表示法执行相同操作的笨拙性。
(b) 当我读到 len(x) 的代码时,我知道它正在请求某个对象的长度。这告诉我两件事:结果是一个整数,参数是某种容器。相反,当我读到 x.len() 时,我必须已经知道 x 是某种实现接口的容器,或者从具有标准 len() 的类继承而来。当未实现映射的类具有 get() 或 keys() 方法,或者非文件具有 write() 方法时,我们偶尔会感到困惑。
换个角度说,我认为“len”是一种内置操作。我不想失去它。/…/
解决方案 3:
Python 是一门实用的编程语言,而、等len()
作为函数而不是方法的str
原因都是实用的。list
`dict`
内置len()
函数直接处理内置类型:的 CPython 实现实际上返回C 结构中表示内存中任何可变大小内置对象的len()
字段的值。这比调用方法要快得多——无需进行属性查找。获取集合中的项目数是一种常见操作,必须有效地处理诸如、等基本和多样化类型。ob_size
`PyVarObjectstr
list`array.array
但是,为了促进一致性,当应用于len(o)
用户定义类型时,Python 会调用o.__len__()
作为后备。 __len__
并且Python 数据模型__abs__
中记录的所有其他特殊方法可以轻松创建行为类似于内置函数的对象,从而实现我们称之为“Pythonic”的富有表现力且高度一致的 API。
通过实现特殊方法,您的对象可以支持迭代、重载中缀运算符、管理with
块中的上下文等。您可以将数据模型视为使用 Python 语言本身作为框架的一种方式,您创建的对象可以在其中无缝集成。
第二个原因是,它比更容易阅读和书写,Guido van Rossum 的这句名言就是支持这一观点的。len(s)
`s.len()`
该符号len(s)
与带前缀符号的一元运算符一致,例如abs(n)
.len()
的使用频率远远高于abs()
,因此它应该同样易于书写。
也可能有历史原因:在 Python 之前的 ABC 语言中(并且在其设计中非常有影响力),有一个一元运算符写为#s
which 表示len(s)
。
解决方案 4:
有一个len
方法:
>>> a = 'a string of some length'
>>> a.__len__()
23
>>> a.__len__
<method-wrapper '__len__' of str object at 0x02005650>
解决方案 5:
met% python -c 'import this' | grep 'only one'
There should be one-- and preferably only one --obvious way to do it.
解决方案 6:
其余答案中缺少了一些东西:len
函数检查方法__len__
是否返回非负数int
。事实上,这len
是一个函数,这意味着类不能覆盖此行为以避免检查。因此,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
通过将函数重新分配为全局变量来“覆盖”该函数,但这样做的代码比覆盖类中的方法的代码更明显可疑。
解决方案 7:
这里有一些很棒的答案,因此在我给出自己的答案之前,我想强调一下我在这里读过的一些精彩内容(无意双关)。
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': 'stackoverflow'})
>>> 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
扫码咨询,免费领取项目管理大礼包!