Разница между len () и .__ len __ ()? - PullRequest
79 голосов
/ 20 марта 2010

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

Если нет видимой разницы, что происходит за кулисами по-другому?

Ответы [ 4 ]

87 голосов
/ 20 марта 2010

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

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

62 голосов
/ 20 марта 2010

Часто случается, что «типичным» поведением встроенного оператора или оператора является вызов (с другим и более приятным синтаксисом) подходящих магических методов (с именами, такими как __whatever__) для задействованных объектов. Часто встроенный оператор или оператор имеет «добавленную стоимость» (он может выбирать различные пути в зависимости от задействованных объектов) - в случае len против __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.* магический метод правильно назван __next__ вместо "упс-эй" next! -).

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

20 голосов
/ 20 марта 2010

Вы можете считать, что 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)

Хотя поведение немного иное. Например, в случае целых чисел

>>> (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 голосов
/ 18 февраля 2015

Вы можете проверить Документы 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...