我知道python有一个len()
用于确定字符串大小的函数,但我想知道为什么它不是字符串对象的方法。
更新
好吧,我意识到我大错特错了。__len__()
实际上是一个字符串对象的方法。在 Python 中使用字符串对象上的 len 函数来查看面向对象的代码似乎很奇怪。__len__
此外,将其视为名称而不是 len也很奇怪。
我知道python有一个len()
用于确定字符串大小的函数,但我想知道为什么它不是字符串对象的方法。
好吧,我意识到我大错特错了。__len__()
实际上是一个字符串对象的方法。在 Python 中使用字符串对象上的 len 函数来查看面向对象的代码似乎很奇怪。__len__
此外,将其视为名称而不是 len也很奇怪。
字符串确实有一个长度方法:__len__()
Python 中的协议是在具有长度的对象上实现此方法并使用内置len()
函数为您调用它,类似于您实现__iter__()
和使用内置iter()
函数的方式(或在后面调用该方法你的场景)在可迭代的对象上。
有关更多信息,请参阅模拟容器类型。
这是关于 Python 协议主题的好读物:Python and the Principle of Least Astonishment
吉姆对这个问题的回答可能会有所帮助;我在这里复制它。引用 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”视为内置操作。我不想失去那个。/…/
有一个len
方法:
>>> a = 'a string of some length'
>>> a.__len__()
23
>>> a.__len__
<method-wrapper '__len__' of str object at 0x02005650>
Python 是一种实用的编程语言,len()
作为函数而不是 , 等的方法的str
原因list
是dict
实用的。
len()
内置函数直接处理内置类型:CPython 实现len()
实际上返回C 结构中的字段值,该ob_size
字段表示内存中任何可变大小的内置对象。这比调用方法要快得多——不需要进行属性查找。获取集合中项目的数量是一种常见的操作,并且对于诸如 , 等基本和多样化的类型必须有效地工作。PyVarObject
str
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)
.
met% python -c 'import this' | grep 'only one'
There should be one-- and preferably only one --obvious way to do it.
这里有一些很好的答案,所以在我给出自己的答案之前,我想强调一些我在这里读过的宝石(不是红宝石双关语)。
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
这里的其他答案缺少一些东西:该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
通过将函数重新分配为全局变量来“覆盖”该函数,但是这样做的代码比覆盖类中方法的代码更明显可疑。
你也可以说
>> x = 'test'
>> len(x)
4
使用 Python 2.7.3。
它没有?
>>> "abc".__len__()
3