Разница между len() и.__ len __()?

Есть ли разница между вызовом len([1,2,3]) или [1,2,3].__len__()? Если нет видимой разницы, что делается по-разному за кулисами?

Ответ 1

len - это функция для получения длины коллекции. Он работает, вызывая метод объекта __len__. __something__ атрибуты являются особыми и обычно больше, чем кажется глазу, и обычно их не следует вызывать напрямую.

В какой-то момент было принято решение о том, что длина чего-то должна быть функцией, а не кодом метода, полагая, что значение len(a) будет понятным для новичков, но a.len() не будет столь ясным. Когда запуск Python __len__ даже не существовал, а len - это особая вещь, которая работала с несколькими типами объектов. Независимо от того, осталась ли у нас ситуация, общий смысл, он должен остаться.

Ответ 2

Часто бывает, что "типичное" поведение встроенного или оператора должно вызывать (с другим и более сильным синтаксисом) подходящие магические методы (с именами типа __whatever__) на объектах. Часто встроенный или оператор имеет "добавленное значение" (он может принимать разные пути в зависимости от задействованных объектов) - в случае len vs __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, вы уверен, который, если программа продолжает после этого, а не создает исключение, вызов возвращает целое число, -negative и менее 2 ** 31 - когда вы видите вызов xxx.__len__(), у вас нет уверенности (кроме того, что автор кода либо незначителен с Python, либо не имеет ничего хорошего; -).

Другие встроенные функции обеспечивают еще большую добавленную стоимость, помимо простых проверок и удобочитаемости. Благодаря равномерному проектированию всего Python для работы с помощью вызовов встроенных функций и использования операторов, никогда не используя вызовы магических методов, программисты избавляются от бремени запоминания, какой случай является тем. (Иногда ошибка проскальзывает: до 2,5, вы должны были вызвать foo.next() - в 2.6, в то время как это все еще работает для обратной совместимости, вы должны вызвать next(foo), а в 3.* магический метод правильно назван __next__ вместо "oops-ey" next! -).

Таким образом, общее правило заключается в том, чтобы никогда не вызвать магический метод напрямую (но всегда косвенно через встроенный), если вы точно не знаете, зачем вам это нужно (например, когда вы переопределяете такой метод в подклассе, если подкласс должен отложить до суперкласса, который должен выполняться посредством явного вызова магического метода).

Ответ 3

Вы можете думать о 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)

Однако есть немного другое поведение. Например, в случае 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()

Ответ 4

Вы можете проверить Документы Pythond:

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