len()和.__ len __()之间的区别?

问题:len()和.__ len __()之间的区别?

呼叫len([1,2,3])或之间有什么区别[1,2,3].__len__()

如果没有明显的差异,那么幕后的操作又有什么不同?

Is there any difference between calling len([1,2,3]) or [1,2,3].__len__()?

If there is no visible difference, what is done differently behind the scenes?


回答 0

len是获取集合长度的函数。它通过调用对象的__len__方法来工作。__something__属性是特殊的,通常不仅仅令人眼花,乱,通常不应直接调用。

它是在很久以前的某个时刻决定的,某个东西的长度应该是一个函数,而不是方法代码,理由是len(a)初学者很清楚,但a.len()不清楚。Python启动__len__时甚至还不存在,这len是一种特殊的东西,可以处理几种类型的对象。不管情况是否如此,从总体上讲都是合理的,它将继续存在。

len is a function to get the length of a collection. It works by calling an object’s __len__ method. __something__ attributes are special and usually more than meets the eye, and generally should not be called directly.

It was decided at some point long ago getting the length of something should be a function and not a method code, reasoning that len(a)‘s meaning would be clear to beginners but a.len() would not be as clear. When Python started __len__ didn’t even exist and len was a special thing that worked with a few types of objects. Whether or not the situation this leaves us makes total sense, it’s here to stay.


回答 1

通常,内置或运算符的“典型”行为是__whatever__在所涉及的对象上调用(使用不同且更好的语法)合适的魔术方法(名称类似的方法)。内置函数或运算符通常具有“附加值”(根据所涉及的对象,它可以采用不同的路径)–在lenvs 的情况下,__len__内置函数缺少一些完整性检查魔术方法:

>>> class bah(object):
...   def __len__(self): return "an inch"
... 
>>> bah().__len__()
'an inch'
>>> len(bah())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object cannot be interpreted as an integer

当您看到对len内置函数的调用时,可以确定,如果程序在此之后继续执行而不是引发异常,则该调用返回的是整数,非负且小于2 ** 31-您会看到对的调用xxx.__len__(),但不确定(除非代码的编写者不熟悉Python或根本不行;-)。

除了简单的健全性检查和可读性之外,其他内置组件还提供更多的附加值。通过统一地设计所有Python以通过调用内建函数和使用运算符来工作,而不是通过调用魔术方法来工作,程序员可以免于记住哪种情况的负担。(有时会出现错误:直到2.5,您必须foo.next()在2.6中调用-在仍然向后兼容的情况下,您应该调用next(foo),并且在中3.*,magic方法已正确命名,__next__而不是“ oops-ey” next!- )。

因此,一般的规则应该是永远不要直接调用魔术方法(而总是通过内置方法间接调用),除非您确切知道为什么需要这样做(例如,当您在子类中覆盖此类方法时,如果子类需要遵循必须通过显式调用magic方法来完成的超类)。

It’s often the case that the “typical” behavior of a built-in or operator is to call (with different and nicer syntax) suitable magic methods (ones with names like __whatever__) on the objects involved. Often the built-in or operator has “added value” (it’s able to take different paths depending on the objects involved) — in the case of len vs __len__, it’s just a bit of sanity checking on the built-in that is missing from the magic method:

>>> class bah(object):
...   def __len__(self): return "an inch"
... 
>>> bah().__len__()
'an inch'
>>> len(bah())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object cannot be interpreted as an integer

When you see a call to the len built-in, you’re sure that, if the program continues after that rather than raising an exception, the call has returned an integer, non-negative, and less than 2**31 — when you see a call to xxx.__len__(), you have no certainty (except that the code’s author is either unfamiliar with Python or up to no good;-).

Other built-ins provide even more added value beyond simple sanity checks and readability. By uniformly designing all of Python to work via calls to builtins and use of operators, never through calls to magic methods, programmers are spared from the burden of remembering which case is which. (Sometimes an error slips in: until 2.5, you had to call foo.next() — in 2.6, while that still works for backwards compatibility, you should call next(foo), and in 3.*, the magic method is correctly named __next__ instead of the “oops-ey” next!-).

So the general rule should be to never call a magic method directly (but always indirectly through a built-in) unless you know exactly why you need to do that (e.g., when you’re overriding such a method in a subclass, if the subclass needs to defer to the superclass that must be done through explicit call to the magic method).


回答 2

您可以认为len()大致等同于

def len(x):
    return x.__len__()

优点之一是它允许您编写类似

somelist = [[1], [2, 3], [4, 5, 6]]
map(len, somelist) 

代替

map(list.__len__, somelist)

要么

map(operator.methodcaller('__len__'), somelist)

但是,行为略有不同。例如int的情况

>>> (1).__len__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute '__len__'
>>> len(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'int' has no len()

You can think of len() as being roughly equivalent to

def len(x):
    return x.__len__()

One advantage is that it allows you to write things like

somelist = [[1], [2, 3], [4, 5, 6]]
map(len, somelist) 

instead of

map(list.__len__, somelist)

or

map(operator.methodcaller('__len__'), somelist)

There is slightly different behaviour though. For example in the case of ints

>>> (1).__len__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute '__len__'
>>> len(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'int' has no len()

回答 3

您可以检查Pythond docs

>>> class Meta(type):
...    def __getattribute__(*args):
...       print "Metaclass getattribute invoked"
...       return type.__getattribute__(*args)
...
>>> class C(object):
...     __metaclass__ = Meta
...     def __len__(self):
...         return 10
...     def __getattribute__(*args):
...         print "Class getattribute invoked"
...         return object.__getattribute__(*args)
...
>>> c = C()
>>> c.__len__()                 # Explicit lookup via instance
Class getattribute invoked
10
>>> type(c).__len__(c)          # Explicit lookup via type
Metaclass getattribute invoked
10
>>> len(c)                      # Implicit lookup
10

You can check Pythond docs:

>>> class Meta(type):
...    def __getattribute__(*args):
...       print "Metaclass getattribute invoked"
...       return type.__getattribute__(*args)
...
>>> class C(object):
...     __metaclass__ = Meta
...     def __len__(self):
...         return 10
...     def __getattribute__(*args):
...         print "Class getattribute invoked"
...         return object.__getattribute__(*args)
...
>>> c = C()
>>> c.__len__()                 # Explicit lookup via instance
Class getattribute invoked
10
>>> type(c).__len__(c)          # Explicit lookup via type
Metaclass getattribute invoked
10
>>> len(c)                      # Implicit lookup
10