Почему код Python использует функцию len () вместо метода длины?

Я знаю, что в python есть функция len(), которая используется для определения размера строки, но мне было интересно, почему это не метод строкового объекта.

Обновить

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


person fuentesjr    schedule 25.10.2008    source источник
comment
Относится к вашему обновлению: Есть ли случай, когда len(someObj) не вызывает функцию someObj __len__? tl; dr Да, поэтому всегда используйте len() вместо __len__().   -  person wjandrea    schedule 04.07.2020


Ответы (9)


У строк есть метод длины: __len__()

Протокол в Python заключается в реализации этого метода на объектах, имеющих длину, и использовании встроенного _ 2_, которая вызывает ее за вас, аналогично тому, как вы бы реализовали __iter__() и использовали встроенную функцию iter() (или если бы метод был вызван за кулисами для вас) на объектах, которые можно повторять.

Дополнительную информацию см. В разделе Эмуляция типов контейнеров.

Вот хорошее прочтение на тему протоколов в Python: Python и принцип наименьшего удивления

person Jonny Buchanan    schedule 25.10.2008
comment
Меня удивляет, насколько глупой является причина использования len. Они думают, что легче заставить людей реализовывать .__len__, чем заставлять людей внедрять .len(). Это то же самое, и каждый выглядит намного чище. Если в языке будет ООП __len__, то какой смысл делать len(..) - person alternative; 08.11.2011
comment
len, str и т. д. могут использоваться с функциями более высокого порядка, такими как map, reduce и filter, без необходимости определять функцию или лямбда только для вызова метода. Не все вращается вокруг ООП, даже в Python. - person Evicatos; 07.11.2013
comment
Кроме того, используя протокол, они могут предоставить альтернативные способы реализации вещей. Например, вы можете создать итерацию с __iter__ или только с __getitem__, и iter(x) будет работать в любом случае. Вы можете создать объект, пригодный для использования в контексте bool, с помощью __bool__ или __len__, и bool(x) будет работать в любом случае. И так далее. Я думаю, что Армин достаточно хорошо объясняет это в связанном посте, но даже если бы он этого не сделал, называть Python дебилом из-за стороннего объяснения парня, который часто публично не в ладах с основными разработчиками, было бы не совсем справедливым ... - person abarnert; 04.11.2014
comment
@Evicatos: +1 за Не все вращается вокруг ООП, но я бы остановился на особенно в Python, а не на даже. Python (в отличие от Java, Ruby или Smalltalk) не пытается быть чистым языком ООП; он явно разработан как мультипарадигмальный язык. Составление списков - это не методы для итераций. zip - это функция верхнего уровня, а не zip_with метод. И так далее. То, что все является объектом, не означает, что быть объектом - это самое важное в каждой вещи. - person abarnert; 04.11.2014
comment
Эта статья написана так плохо, что я смущаюсь и злюсь после попытки ее прочитать. В Python 2.x тип Tuple, например, не предоставляет никаких неспециальных методов, и тем не менее вы можете использовать его для создания из него строки: все это представляет собой объединение почти не поддающихся расшифровке предложений. - person MirroredFate; 05.02.2016

Ответ Джима на этот вопрос может помочь; Копирую сюда. Цитата Гвидо ван Россума:

Прежде всего, я выбрал len (x) вместо x.len () по причинам HCI (def __len __ () появился намного позже). На самом деле есть две взаимосвязанные причины, обе - HCI:

(а) Для некоторых операций префиксная нотация просто читается лучше, чем постфиксная - префиксные (и инфиксные!) операции имеют давнюю традицию в математике, которой нравятся нотации, в которых визуальные элементы помогают математику думать о проблеме. Сравните простоту, с которой мы переписываем формулу типа x * (a + b) в x a + x b, с неуклюжестью выполнения того же самого с использованием необработанной объектно-ориентированной нотации.

(б) Когда я читаю код, который говорит len (x), я знаю, что он запрашивает длину чего-то. Это говорит мне о двух вещах: результат - целое число, а аргумент - это своего рода контейнер. Напротив, когда я читаю x.len (), я уже должен знать, что x - это своего рода контейнер, реализующий интерфейс или наследующий от класса, имеющего стандартный len (). Обратите внимание на путаницу, которую мы иногда испытываем, когда класс, не реализующий сопоставление, имеет метод get () или keys (), или что-то, что не является файлом, имеет метод write ().

Говоря то же самое по-другому, я вижу «len» как встроенную операцию. Я бы не хотел это потерять. /… /

person Federico A. Ramponi    schedule 26.10.2008

Есть len метод:

>>> a = 'a string of some length'
>>> a.__len__()
23
>>> a.__len__
<method-wrapper '__len__' of str object at 0x02005650>
person unmounted    schedule 25.10.2008
comment
Да, но он не предназначен для прямого использования. len проверяет вывод __len__. См. Есть ли случай, когда len(someObj) не вызывает someObj функцию __len__? Обычно методы Dunder не предназначены для прямого вызова. - person wjandrea; 04.07.2020

Python - это прагматичный язык программирования, и причины того, что len() является функцией, а не методом str, list, dict и т. Д., Прагматичны.

Встроенная функция len() имеет дело непосредственно со встроенными типами: реализация len() в CPython фактически возвращает значение поля ob_size в _ 8_ структуру C, которая представляет любой встроенный объект переменного размера в памяти. Это намного быстрее, чем вызов метода - поиск атрибутов не требуется. Получение количества элементов в коллекции - обычная операция, которая должна эффективно работать для таких основных и разнообразных типов, как str, list, array.array и т. Д.

Однако для обеспечения согласованности при применении len(o) к определяемому пользователем типу Python вызывает o.__len__() в качестве запасного варианта. __len__, __abs__ и все другие специальные методы, описанные в модели данных Python, упрощают создание объектов, которые ведут себя как встроенные модули, обеспечивая выразительные и согласованные API-интерфейсы, которые мы называем «Pythonic».

Реализуя специальные методы, ваши объекты могут поддерживать итерацию, перегружать инфиксные операторы, управлять контекстами в блоках with и т. Д. Вы можете думать о модели данных как о способе использования самого языка Python как фреймворка, в котором объекты создаваемое вами, можно легко интегрировать.

Вторая причина, подтвержденная цитатами Гвидо ван Россума, например этот, заключается в том, что len(s) легче читать и писать, чем s.len().

Обозначение len(s) согласуется с унарными операторами с префиксной записью, например abs(n). len() используется гораздо чаще, чем abs(), и его следует так же легко написать.

Также может быть историческая причина: в языке ABC, который предшествовал Python (и имел большое влияние на его дизайн), был унарный оператор, записанный как #s, что означало len(s).

person Luciano Ramalho    schedule 21.04.2014
comment
Я не верю аргументу оптимальности. Хотя вызов метода действительно может быть медленнее, достаточно продвинутый интерпретатор или компилятор - а интерпретаторы Python делают гораздо более сложные вещи, чем этот - могут распознать, что вы вызываете метод len встроенного типа, и вернуться к возврату ob_size или чего-то еще. нужды, по крайней мере, в подавляющем большинстве случаев. - person dionyziz; 03.11.2020
comment
@dionyziz: алгоритм Python для поиска атрибутов очень гибкий. У вас есть атрибуты экземпляра, атрибуты класса, дескрипторы данных, дескрипторы не-данных и множественное наследование. Все это может играть роль в вызове типа s.len(), но не влияет на len(s). - person Luciano Ramalho; 15.02.2021

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

  • Python не является чистым языком ООП - это универсальный, многопарадигмальный язык, который позволяет программисту использовать парадигму, с которой он наиболее удобен, и / или парадигму, которая лучше всего подходит для их решения.
  • Python имеет первоклассные функции, поэтому len на самом деле является объектом. С другой стороны, в Ruby нет функций первого класса. Таким образом, объект функции len имеет собственные методы, которые вы можете проверить, запустив dir(len).

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

>>> class List(list):
...     def len(self):
...         return len(self)
...
>>> class Dict(dict):
...     def len(self):
...         return len(self)
...
>>> class Tuple(tuple):
...     def len(self):
...         return len(self)
...
>>> class Set(set):
...     def len(self):
...         return len(self)
...
>>> my_list = List([1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'])
>>> my_dict = Dict({'key': 'value', 'site': 'stackoverflow'})
>>> my_set = Set({1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'})
>>> my_tuple = Tuple((1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'))
>>> my_containers = Tuple((my_list, my_dict, my_set, my_tuple))
>>>
>>> for container in my_containers:
...     print container.len()
...
15
2
15
15
person Charles Addis    schedule 28.02.2017

В остальных ответах здесь чего-то не хватает: функция len проверяет, что метод __len__ возвращает неотрицательное значение int. Тот факт, что len является функцией, означает, что классы не могут переопределить это поведение, чтобы избежать проверки. Таким образом, len(obj) обеспечивает уровень безопасности, который obj.len() не может.

Пример:

>>> class A:
...     def __len__(self):
...         return 'foo'
...
>>> len(A())
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    len(A())
TypeError: 'str' object cannot be interpreted as an integer
>>> class B:
...     def __len__(self):
...         return -1
... 
>>> len(B())
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    len(B())
ValueError: __len__() should return >= 0

Конечно, можно «переопределить» функцию len, переназначив ее как глобальную переменную, но код, который делает это, гораздо более подозрительный, чем код, который переопределяет метод в классе.

person kaya3    schedule 02.12.2019
comment
Он также проверяет, что int не больше sys.maxsize и что obj.__len__ существует в первую очередь. Я написал ответ на другой вопрос, в котором перечислены все различные проверки. - person wjandrea; 04.07.2020

Вы также можете сказать

>> x = 'test'
>> len(x)
4

Использование Python 2.7.3.

person SpanosAngelos    schedule 04.01.2013
comment
len функция уже упоминалась в предыдущих ответах. В чем тогда смысл этого ответа? - person Piotr Dobrogost; 11.09.2014

Это не так?

>>> "abc".__len__()
3
person Nick Stinemates    schedule 26.10.2008
comment
Конечно, но он не предназначен для прямого вызова. См. Есть ли случай, когда len(someObj) не вызывает someObj функцию __len__? Обычно методы Dunder не предназначены для прямого вызова. - person wjandrea; 04.07.2020

person    schedule
comment
Самая очевидная вещь при работе с объектом - это, конечно же, метод. - person Peter Cooper; 06.05.2012
comment
@Peter: Я бы заплатил 20 долларов любому, у кого есть фото-доказательства того, что они приклеили ваш комментарий к спине Гвидо. 50 долларов, если он у него на лбу. - person bug; 28.01.2013
comment
Да, дизайнеры Python придерживаются догмы, но сами не уважают собственную догму. - person Alex Bitek; 05.02.2013
comment
$ python -c 'import this' | grep очевидно - person Ed Randall; 10.06.2016