为什么Python代码使用len()函数而不是length方法?

问题:为什么Python代码使用len()函数而不是length方法?

我知道python具有len()用于确定字符串大小的函数,但是我想知道为什么它不是字符串对象的方法。

更新资料

好吧,我意识到我很尴尬地犯了错误。__len__()实际上是字符串对象的方法。在字符串对象上使用len函数在Python中看到面向对象的代码似乎很奇怪。此外,看到__len__名字而不是len 也很奇怪。

I know that python has a len() function that is used to determine the size of a string, but I was wondering why it’s not a method of the string object.

Update

Ok, I realized I was embarrassingly mistaken. __len__() is actually a method of a string object. It just seems weird to see object oriented code in Python using the len function on string objects. Furthermore, it’s also weird to see __len__ as the name instead of just len.


回答 0

字符串确实有一个length方法: __len__()

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

有关更多信息,请参见模拟容器类型

这是有关Python协议主题的好书:Python和最小惊讶原则

Strings do have a length method: __len__()

The protocol in Python is to implement this method on objects which have a length and use the built-in len() function, which calls it for you, similar to the way you would implement __iter__() and use the built-in iter() function (or have the method called behind the scenes for you) on objects which are iterable.

See Emulating container types for more information.

Here’s a good read on the subject of protocols in Python: Python and the Principle of Least Astonishment


回答 1

吉姆对这个问题的回答可能会有所帮助。我在这里复制。引用Guido van Rossum:

首先,出于HCI的原因,我选择len(x)而不是x.len()(def __len __()来得很晚)。实际上,两个HCI相互交织在一起:

(a)对于某些运算,前缀表示法比后缀读得更好-前缀(和infix!)运算符在数学中有很长的传统,喜欢在视觉上帮助数学家思考问题的表示法。将我们将x *(a + b)之类的公式重写为x a + x b 的简便性与使用原始OO符号做相同事情的笨拙性进行比较。

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

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

Jim’s answer to this question may help; I copy it here. Quoting Guido van Rossum:

First of all, I chose len(x) over x.len() for HCI reasons (def __len__() came much later). There are two intertwined reasons actually, both HCI:

(a) For some operations, prefix notation just reads better than postfix — prefix (and infix!) operations have a long tradition in mathematics which likes notations where the visuals help the mathematician thinking about a problem. Compare the easy with which we rewrite a formula like x*(a+b) into xa + xb to the clumsiness of doing the same thing using a raw OO notation.

(b) When I read code that says len(x) I know that it is asking for the length of something. This tells me two things: the result is an integer, and the argument is some kind of container. To the contrary, when I read x.len(), I have to already know that x is some kind of container implementing an interface or inheriting from a class that has a standard len(). Witness the confusion we occasionally have when a class that is not implementing a mapping has a get() or keys() method, or something that isn’t a file has a write() method.

Saying the same thing in another way, I see ‘len‘ as a built-in operation. I’d hate to lose that. /…/


回答 2

有一种len方法:

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

There is a len method:

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

回答 3

Python是一种务实的编程语言,并为原因len()是一个功能,而不是一个方法strlistdict等务实。

len()内置函数直接处理的内置类型:CPython的执行len()实际返回的值ob_size字段中PyVarObject的C结构代表任意可变大小的内置存储器中的对象。这是很多比调用一个方法快-无属性的查找需要发生。获取集合中的项目数是一种常见的操作,必须对这些基本类型多样为提高工作效率strlistarray.array等。

但是,为了提高一致性,当应用len(o)到用户定义的类型时,Python会o.__len__()作为后备调用。 __len____abs__和所有其他特殊的记录方法的Python数据模型可以很容易地创建对象,其行为像内置插件,使表现力和高度一致的API,我们称之为“Python化”。

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

第二个原因,通过报价从吉多·范罗苏姆等支撑这一个,是它更容易阅读和写len(s)s.len()

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

可能还有一个历史原因:在Python之前的ABC语言中(在其设计中很有影响力),有一个一元运算符,#s其含义为len(s)

Python is a pragmatic programming language, and the reasons for len() being a function and not a method of str, list, dict etc. are pragmatic.

The len() built-in function deals directly with built-in types: the CPython implementation of len() actually returns the value of the ob_size field in the PyVarObject C struct that represents any variable-sized built-in object in memory. This is much faster than calling a method — no attribute lookup needs to happen. Getting the number of items in a collection is a common operation and must work efficiently for such basic and diverse types as str, list, array.array etc.

However, to promote consistency, when applying len(o) to a user-defined type, Python calls o.__len__() as a fallback. __len__, __abs__ and all the other special methods documented in the Python Data Model make it easy to create objects that behave like the built-ins, enabling the expressive and highly consistent APIs we call “Pythonic”.

By implementing special methods your objects can support iteration, overload infix operators, manage contexts in with blocks etc. You can think of the Data Model as a way of using the Python language itself as a framework where the objects you create can be integrated seamlessly.

A second reason, supported by quotes from Guido van Rossum like this one, is that it is easier to read and write len(s) than s.len().

The notation len(s) is consistent with unary operators with prefix notation, like abs(n). len() is used way more often than abs(), and it deserves to be as easy to write.

There may also be a historical reason: in the ABC language which preceded Python (and was very influential in its design), there was a unary operator written as #s which meant len(s).


回答 4

met% python -c 'import this' | grep 'only one'
There should be one-- and preferably only one --obvious way to do it.
met% python -c 'import this' | grep 'only one'
There should be one-- and preferably only one --obvious way to do it.

回答 5

这里有一些很好的答案,因此在我给出自己的名字之前,我想重点介绍一下我在这里读过的一些宝石(无红宝石双关语)。

  • 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

There are some great answers here, and so before I give my own I’d like to highlight a few of the gems (no ruby pun intended) I’ve read here.

  • Python is not a pure OOP language — it’s a general purpose, multi-paradigm language that allows the programmer to use the paradigm they are most comfortable with and/or the paradigm that is best suited for their solution.
  • Python has first-class functions, so len is actually an object. Ruby, on the other hand, doesn’t have first class functions. So the len function object has it’s own methods that you can inspect by running dir(len).

If you don’t like the way this works in your own code, it’s trivial for you to re-implement the containers using your preferred method (see example below).

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

回答 6

你也可以说

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

使用Python 2.7.3。

You can also say

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

Using Python 2.7.3.


回答 7

这里的其余答案缺少一些内容: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通过将其重新分配为全局变量来“覆盖” 函数,但是比起覆盖类中方法的代码,这样做的代码明显更可疑。

Something missing from the rest of the answers here: the len function checks that the __len__ method returns a non-negative int. The fact that len is a function means that classes cannot override this behaviour to avoid the check. As such, len(obj) gives a level of safety that obj.len() cannot.

Example:

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

Of course, it is possible to “override” the len function by reassigning it as a global variable, but code which does this is much more obviously suspicious than code which overrides a method in a class.


回答 8

不是吗

>>> "abc".__len__()
3

It doesn’t?

>>> "abc".__len__()
3