Різниця між len () та .__ len __ ()?


Відповіді:


102

len- це функція для отримання довжини колекції. Він працює за допомогою виклику __len__методу об’єкта . __something__атрибути особливі і зазвичай більше, ніж зустрічають око, і взагалі їх не слід називати безпосередньо.

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


66

Часто трапляється так, що "типова" поведінка вбудованого або оператора полягає в тому, щоб викликати (з різним і приємнішим синтаксисом) відповідні магічні методи (ті з назвами, як __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.*, магічний метод правильно названий __next__замість "oops-ey" next! - ).

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


Я початківець користувач Python (не думав початківець програміст), і я не впевнений у тому, що "коли ви побачите дзвінок до вбудованого len, ви впевнені, що якщо програма продовжиться після цього, а не збільшує виняток". Я спробував це: def len(x): return "I am a string." print(len(42)) print(len([1,2,3]))і надрукував I am stringдвічі. Чи можете ви пояснити це більше?
Darek Nędza

4
@ DarekNędza Це не має нічого спільного з вищезазначеним, що стосується вбудованого об'єму. Ви щойно визначили свою функцію ліниву, яка, звичайно, може повернути все, що ви хочете. ОП розповіла про вбудований об'єкт , який викликає __len__спеціальний метод (не функціонування) на розглянутому об'єкті.
Векі

@Veky Як я можу бути впевнений, що я викликаю вбудовану функцію, а lenне якусь іншу функцію (як, наприклад, у моєму прикладі), у якої було таке ім'я - len. Немає попередження на кшталт "Ви переосмислюєте вбудовану функцію" або щось подібне. На мою думку, я не можу бути впевненим у тому, що сказав Алекс у своїй відповіді.
Darek Nędza

3
Алекс прямо сказав, що якщо ви телефонуєте вбудованим, то ви впевнені ..._. Він нічого не сказав про те, що ти впевнений, що ти телефонуєш вбудованим. Але якщо ви хочете знати , що ви можете: len in vars(__builtins__).values().
Веки

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)

Однак є дещо інша поведінка. Наприклад у випадку з інтами

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