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