#python #python_internals
В чем разница между len() и .__len__()? И могут ли они возвращать разные значения?
Ответы
Ответ 1
__len__ это магический метод, который реализует len операцию. Как и любой другой специальный метод, он вызывается специальным образом (должен быть определён в самом классе), то есть len(o) не всегда эквивалентно o.__len__(). Подробнее в ответе о магических методах. Дополнительно, значения len() ограничены sys.maxsize: >>> import sys >>> class Big: ... def __len__(self): ... return sys.maxsize + 1 ... >>> len(Big()) Traceback (most recent call last) ... OverflowError: cannot fit 'int' into an index-sized integer >>> Big().__len__() 9223372036854775808 На практике, иногда полезно иметь последовательность с большой длиной: Get the highest possible gmtime for any architecture Weighted random sample in pythonОтвет 2
Для встроенных классов (list, dict, tuple, ...) функция len(o) не вызывает o.__len__(), а напрямую вызывает функцию o->tp_as_sequence->sq_length(o) (реализована на С) (благодарю @jfs за подсказку). Для остальных классов функция len(o) вызывает o.__len__(). Интересное наблюдение: In [137]: l = list(range(10**6)) In [138]: %timeit len(l) The slowest run took 8.91 times longer than the fastest. This could mean that an intermediate result is being cached. 1000000 loops, best of 3: 314 ns per loop In [139]: %timeit l.__len__() The slowest run took 8.42 times longer than the fastest. This could mean that an intermediate result is being cached. 1000000 loops, best of 3: 388 ns per loop Похоже для объектов встроенных классов obj.__len__() немного медленнее по сравнению с len(obj). Если же реализовать __len__ в собственном классе, то будет наоборот, т.к. len(obj) это обертка (wrapper) над obj.__len__() (для объектов "не встроенных" классов) Пример: In [152]: class my_len: ...: def __len__(self): ...: return 1 ...: In [153]: a = my_len() In [154]: %timeit len(a) The slowest run took 8.22 times longer than the fastest. This could mean that an intermediate result is being cached. 1000000 loops, best of 3: 624 ns per loop In [155]: %timeit a.__len__() The slowest run took 8.95 times longer than the fastest. This could mean that an intermediate result is being cached. 1000000 loops, best of 3: 365 ns per loop UPDATE: вот ответ поясняющий почему len(o) для встроенных типов работает быстрее чем o.__len__(): The builtin len() function does not look up the .__len__ attribute. It looks up the tp_as_sequence pointer, which in turn has a sq_length attribute. The .__len__ attribute on built-in objects is indirectly mapped to the same slot, and it is that indirection (plus the attribute lookup) that takes more time. For Python-defined classes, the type object looks up the .__len__ method when the sq_length is requested.Ответ 3
да они возвращают разные значения. Например len() возвращает количество елементов в списке >>> s = ['a', 'b', 'c', 'd', 'q'] >>> len(s) 5 А метод len может использоваться только в классе и для списков возвращает его содержимое >>> class qwerty: def __len__(self): q = ['a', 'b', 'c', 'd', 'q'] return q >>> qwerty().__len__() ['a', 'b', 'c', 'd', 'q'] >>>
Комментариев нет:
Отправить комментарий