Я недавно играл с Python, и одна вещь, которую я нахожу немного странной, - это широкое использование `` магических методов '', например, чтобы сделать его длину доступной, объект реализует метод def __len__(self)
, а затем он вызывается, когда ты пишешь len(obj)
.
Мне просто было интересно, почему объекты просто не определяют len(self)
метод и не вызывают его напрямую как член объекта, например obj.len()
? Я уверен, что у Python должны быть веские причины для того, чтобы делать это именно так, но, как новичок, я еще не понял, что это такое.
python
magic-methods
Грег Бич
источник
источник
len()
илиreversed()
применимое ко многим типам объектов, но такой методappend()
применяется только к последовательностям и т. Д.Ответы:
AFAIK
len
является особенным в этом отношении и имеет исторические корни.Вот цитата из FAQ :
Другие «магические методы» (на самом деле называемые в фольклоре Python специальным методом ) имеют много смысла, и аналогичные функции существуют и в других языках. В основном они используются для кода, который вызывается неявно при использовании специального синтаксиса.
Например:
и так далее...
источник
Из дзен Python:
Это одна из причин - с помощью пользовательских методов, разработчики могли бы свободно выбрать другое имя методы, как
getLength()
,length()
,getlength()
или вообще. Python обеспечивает строгое именование, чтобыlen()
можно было использовать общую функцию .Все операции, общие для многих типов объектов, помещены в магические методы, например
__nonzero__
,__len__
или__repr__
. Однако в большинстве случаев они необязательны.Перегрузка операторов также выполняется с помощью магических методов (например
__le__
), поэтому имеет смысл использовать их и для других общих операций.источник
Python использует слово «волшебные методы» , потому что эти методы действительно творит чудеса для вашей программы. Одним из самых больших преимуществ использования волшебных методов Python является то, что они предоставляют простой способ заставить объекты вести себя как встроенные типы. Это означает, что вы можете избежать уродливых, нелогичных и нестандартных способов выполнения основных операций.
Рассмотрим следующий пример:
Это дает ошибку, потому что тип словаря не поддерживает сложение. Теперь давайте расширим класс словаря и добавим магический метод «__add__» :
Теперь он дает следующий результат.
Таким образом, при добавлении этого метода внезапно произошло волшебство, и ошибка, которую вы получали ранее, исчезла.
Надеюсь, это проясняет вам ситуацию. Для получения дополнительной информации обратитесь к:
Руководство по магическим методам Python (Rafe Kettler, 2012)
источник
Некоторые из этих функций выполняют больше, чем может реализовать один метод (без абстрактных методов в суперклассе). Например,
bool()
действует примерно так:Вы также можете быть на 100% уверены, что
bool()
всегда будет возвращать True или False; если вы полагались на какой-то метод, вы не могли быть полностью уверены, что получите в ответ.Некоторые другие функции, которые имеют относительно сложные реализации (более сложные, чем могут быть лежащие в основе магические методы), - это
iter()
иcmp()
, и все методы атрибутов (getattr
,setattr
иdelattr
). Такие вещи, какint
также доступ к магическим методам при выполнении принуждения (вы можете реализовать__int__
), но они выполняют двойную функцию как типы.len(obj)
на самом деле это единственный случай, от которого я не верюobj.__len__()
.источник
hasattr()
я бы использовалtry:
/except AttributeError:
и вместо этогоif obj.__len__(): return True else: return False
я бы просто сказал,return obj.__len__() > 0
но это просто стилистические вещи.bool(x)
упоминаетсяx.__nonzero__()
) ваш метод не будет работать. У экземпляров bool есть метод__nonzero__()
, и ваш код будет продолжать называть себя, если obj будет bool. Можетbool(obj.__bool__())
быть, к вам следует относиться так же, как и к вам__len__
? (Или этот код действительно работает для Python 3?)len(x)
иx.__len__()
заключается в том, что первый вызовет OverflowError для длин, которые превышаютsys.maxsize
, тогда как последний обычно не будет для типов, реализованных в Python. Однако это скорее ошибка, чем функция (например, объект диапазона Python 3.2 в основном может обрабатывать произвольно большие диапазоны, но использованиеlen
с ними может привести к сбою. Их также__len__
не удается, поскольку они реализованы на C, а не на Python)На самом деле это не «волшебные имена». Это просто интерфейс, который объект должен реализовать для предоставления данной услуги. В этом смысле они не более волшебны, чем любое предопределенное определение интерфейса, которое вам нужно переопределить.
источник
Хотя причина в основном историческая, в Python есть некоторые особенности,
len
из-за которых уместным является использование функции вместо метода.Некоторые операции в Python реализованы как методы, например ,
list.index
иdict.append
, в то время как другие реализуются как и магические вызываемые объекты методы, например ,str
иiter
иreversed
. Эти две группы достаточно различаются, поэтому разный подход оправдан:str
,int
а друзья - типы. Имеет смысл вызвать конструктор.iter
может вызывать,__getitem__
если__iter__
он недоступен, и поддерживает дополнительные аргументы, которые не подходят для вызова метода. По той же причине в последних версиях Pythonit.next()
был изменен наnext(it)
- это имеет больше смысла.__iter__
и__next__
- это называетсяfor
цикл. Для согласованности лучше функция. И делает это лучше при определенных оптимизациях.repr
действует какstr
делает. Наличиеstr(x)
противx.repr()
может сбивать с толку.isinstance
.getattr(x, 'a')
это другой способ работыx.a
иgetattr
обладает многими из вышеупомянутых качеств.Я лично называю первую группу методоподобной, а вторую группу операторной. Это не очень хорошее различие, но я надеюсь, что оно как-то поможет.
Сказав это,
len
не совсем подходит ко второй группе. Он ближе к операциям из первого, с той лишь разницей, что он гораздо более распространен, чем любой из них. Но единственное, что он делает, это звонит__len__
, и это очень близко к этомуL.index
. Однако есть некоторые отличия. Например, он__len__
может быть вызван для реализации других функций, напримерbool
, если метод был вызван,len
вы можете отказатьсяbool(x)
от пользовательскогоlen
метода, который делает совершенно разные вещи.Короче говоря, у вас есть набор очень распространенных функций, которые могут реализовывать классы, к которым можно получить доступ через оператор, через специальную функцию (которая обычно делает больше, чем реализация, как оператор), во время создания объекта, и все они имеют общие черты. Все остальное - метод. И
len
это своего рода исключение из этого правила.источник
К этим двум сообщениям особо нечего добавить, но все "волшебные" функции на самом деле вовсе не волшебные. Они являются частью модуля __ builtins__, который неявно / автоматически импортируется при запуске интерпретатора. Т.е.:
происходит каждый раз перед запуском вашей программы.
Я всегда думал, что было бы правильнее, если бы Python делал это только для интерактивной оболочки и требовал сценариев для импорта различных частей из необходимых им встроенных функций. Также, вероятно, другая обработка __ main__ была бы хороша в оболочках против интерактивных. В любом случае, проверьте все функции и посмотрите, каково это без них:
источник