Новый поиск атрибутов класса стиля для встроенных операций
Рассмотрим следующее различие между классическими классами и новыми классами стилей.
class A():
data = 'abcd'
def __getattr__(self, name):
return getattr(self.data, name)
class B(object):
data = 'abcd'
def __getattr__(self, name):
return getattr(self.data, name)
print(A()[0]) # Prints 'a'
print(B()[0]) # TypeError: 'B' object does not support indexing
Я знаю, что объяснение этого свойства заключается в том, что поиск атрибутов новых объектов стиля начинается в классе, а не в экземплярах для встроенных операций. Но объект класса тоже определил __getattr__ и почему он не вызывается для отсутствующего атрибута, который __getite m__.
2 ответа
Я понял, что ответ заключается в том, что __getattr__ вызывается только в том случае, если поиск атрибута начинается с объекта экземпляра. Но если поиск атрибута явно по классу и экземпляру пропущен, __getattr__ никогда не вызывается.
class B():
data = 'abcd'
def __getattr__(self, name):
print('You are looking for something that doesn\'t exist')
return None
b = B()
b.a
You are looking for something that doesn't exist
B.a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: class B has no attribute 'a'
Следовательно, в классических классах поиск __getitem__ начинается с объекта экземпляра и вызывается __getattr__, тогда как в новом классе стилей поиск начинается с объекта класса и, следовательно, __getattr__ не вызывается.
Как упоминает @Jon в комментариях, вы можете найти ответ в вопросе " Асимметричное поведение для __getattr__
, newstyle vs oldstyle классы и в документации по поиску метода Special для классов нового стиля.
Специальные методы напрямую ищутся в объекте класса по соображениям производительности.
Я хотел бы добавить, что, насколько я знаю, это означает, что вы все еще можете пересылать все не специальные методы в инкапсулированный класс с помощью __getattr__
вам придется явно перенаправить все специальные методы:
class A():
data = 'abcd'
def __getattr__(self, name):
return getattr(self.data, name)
class B(object):
data = 'abcd'
# forward all non-special methods to data
def __getattr__(self, name):
return getattr(self.data, name)
# forward __getitem__ to data
def __getitem__(self, index):
return self.data[index]
print(A()[0]) # Prints 'a'
print(B()[0]) # explicitly defined Prints 'a'
print(B().join([' 1 ',' 2 '])) # forwarded to data prints ' 1 abcd 2 '
Я также хотел бы отметить, что B.data
является атрибутом класса, а не атрибутом экземпляра. Это хорошо для этого примера, но может не соответствовать вашим ожиданиям.