Новый поиск атрибутов класса стиля для встроенных операций

Рассмотрим следующее различие между классическими классами и новыми классами стилей.

   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 является атрибутом класса, а не атрибутом экземпляра. Это хорошо для этого примера, но может не соответствовать вашим ожиданиям.

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