Асимметричное поведение для классов __getattr__, newstyle vs oldstyle - PullRequest
8 голосов
/ 05 августа 2010

это первый раз, когда я пишу здесь, извините, если сообщение не указано или слишком длинное.

Мне было интересно узнать больше о том, как атрибуты объектов выбираются при необходимости. Поэтому я прочитал документацию по Python 2.7 под названием «Модель данных» здесь , я встретил __getattr__ и, чтобы проверить, понял ли я ее поведение, я написал эти простые (и неполные) оболочки строк.

class OldStr:
  def __init__(self,val):
    self.field=val

  def __getattr__(self,name):
    print "method __getattr__, attribute requested "+name

class NewStr(object):
  def __init__(self,val):  
    self.field=val

  def __getattr__(self,name):
    print "method __getattr__, attribute requested "+name

Как вы можете видеть, они одинаковы, за исключением того, что они являются классами старого стиля и нового стиля. Поскольку в цитируемом тексте написано, что __getattr__ «Вызывается, когда поиск атрибута не нашел атрибут в обычных местах», я хотел попробовать операцию + на двух экземплярах этих классов, чтобы посмотреть, что произошло, ожидая идентичного поведения.

Но результаты, которые я получил, меня немного озадачили:

>>> x=OldStr("test")
>>> x+x
method __getattr__, attribute requested __coerce__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable

Хорошо! Я не определил метод для __coerce__ (хотя я ожидал запрос на __add__, неважно :), поэтому __getattr__ вмешался и возвратил бесполезную вещь. Но тогда

>>> y=NewStr("test")
>>> y+y
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'NewStr' and 'NewStr'

Почему это асимметричное поведение между __getattr__ в классах старого и нового классов при использовании встроенного оператора, такого как +? Может ли кто-нибудь помочь мне понять, что происходит, и в чем была моя ошибка при чтении документации?

Большое спасибо, ваша помощь очень ценится!

Ответы [ 3 ]

5 голосов
/ 05 августа 2010

См. Поиск специальных методов для классов нового стиля .

Специальные методы непосредственно ищутся в объекте класса, объекты экземпляра и, следовательно, __getattr__() и __getattribute__() игнорируются.По той же причине instance.__add__ = foobar не работает.

Это сделано для ускорения доступа к атрибутам.Специальные методы вызываются очень часто, и если сделать их объектом стандартного, довольно сложного поиска атрибутов, это значительно замедлит работу интерпретатора.

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

2 голосов
/ 05 августа 2010

Ваши __getattr__ функции ничего не возвращают. Я не знаю, почему класс старого стиля делает __getattr__ перед поиском __add__, но это так, и он пытается вызвать возвращаемое значение, равное None.

Класс нового стиля делает все правильно: вы не определили __add__, поэтому он не знает, как их добавить.

0 голосов
/ 05 августа 2010

Больше информации из документации Python:

http://docs.python.org/reference/datamodel.html#customizing-attribute-access

...