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


Ответы:


104

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

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


67

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

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


Я начинающий пользователь Python (не думал, что начинающий программист), и я не уверен в том, что «когда вы видите вызов встроенного len, вы уверены, что если программа продолжится после этого, а не вызовет исключение». Я пробовал это: def len(x): return "I am a string." print(len(42)) print(len([1,2,3]))и он напечатал I am stringдважды. Вы можете объяснить это подробнее?
Дарек Нендза

4
@ DarekNędza Это не имеет ничего общего с вышеупомянутым, которое касается встроенной len. Вы только что определили свою функцию len, которая, конечно, может возвращать все, что вы хотите. OP говорил о встроенном len, который вызывает __len__специальный метод (не функцию) для рассматриваемого объекта.
Veky

@Veky Как я могу быть уверен, что вызываю встроенную функцию, а lenне какую-то другую функцию (как в моем примере) с таким же именем - len. Нет предупреждений вроде «Вы переопределяете встроенную функцию len» или чего-то подобного. На мой взгляд, я не могу быть уверен в том, что сказал Алекс в своем ответе.
Darek Nędza

3
Алекс прямо сказал, что если вы вызываете встроенный, то вы уверены ..._. Он ничего не сказал о том, что вы звоните встроенным. Но если вы хотите знать , что вы можете: len in vars(__builtins__).values().
Veky

1
К сожалению, это еще один пример отсутствия общего базового класса для объектов в Python. Синтаксическое переключение контекста всегда было чушью. В некоторых случаях это обычная идиома - использовать метод подчеркивания, в других следует использовать что-то вроде функции, чтобы делать что-то общее для многих объектов. Это также странно, потому что многие объекты не имеют смыслового использования для len. Иногда объектная модель больше похожа на C ++, кухня тонкая ..
uchuugaka

28

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

2
Я полагаю, вы имеете в виду operator.methodcallerвместо operator.attrgetter.
Элазар

5

Вы можете проверить документы 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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.