Асимметричное поведение для классов __getattr__, newstyle vs oldstyle

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

Мне было интересно узнать больше о том, как атрибуты объектов выбираются при необходимости. Итак, я прочитал документацию по 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 ответа

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

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

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

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

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

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

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

http://docs.python.org/reference/datamodel.html

Другие вопросы по тегам