Почему объект с переопределенным __getattr__() выбрасывает TypeError?

Вот код

class MyTest: 
    def __init__(self):
         pass

    def __getattr__(self, attr):
        pass
t = MyTest()
print 'my test object: %r' %t

Так что печать вызывает TypeError: 'NoneType' object is not callable в то время как я только хочу видеть, существует ли объект. Конечно, этот код не очень полезен. Но у меня был такой класс заглушки в большой базе кода, поэтому я сделал

if module and module.class and module.class.propery:
   # do something with that property
 ...

и получил Type Error: 'NoneType' object is not callable но линия ничего не вызывает! Я предполагаю, что python неявно вызывает некоторые функции негласно.

Любопытно, что этого не произойдет, если класс наследует от Object

В чем дело?

2 ответа

Решение

В старом стиле, __getattr__ используется для большего разнообразия доступа к атрибутам, включая магические методы. % оператор пытается позвонить t.__repr__() для того, чтобы заполнить %r заполнитель, но t.__repr__ оценивается t.__getattr__('__repr__'), который возвращает None,

в if В этом случае вызывается другой магический метод, но возникает та же проблема.

>>> class Foo:
...   def __getattr__(self, attr):
...     print(attr)
...
>>> f = Foo():
>>> if f:
...   pass
__nonzero__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable

Используйте класс в новом стиле, __getattr__ вызывается только в том случае, если атрибут не может быть найден с помощью обычного метода (проверка __dict__ атрибут экземпляра или любого из классов в MRO экземпляра).

>>> class Foo(object):
...     def __init__(self):
...         self.x = 3
...     def __getattr__(self, attr):
...         print(attr)
...
>>> f = Foo()
>>> if f:
...   pass
...
>>> f.x
3
>>> f.y
y

в if f дело, f сам не реализует __nonzero__ или же __len__и его родитель тоже object, но в этом случае атрибут не используется; дело в том, что f это, по сути, объект используется. В f.x, x находится в атрибуте dict экземпляра, поэтому его значение возвращается напрямую. Только y, который не определен иначе f, Foo, или же objectвызывает вызов __getattr__,

В Python 2, с классами старого стиля, когда вы пытаетесь вызвать __repr__ (при печати) на объекте, у вас есть __getattr__ называется.

Поскольку вы яростно зарезали этот метод, он возвращает None и питон пытается позвонить None (потому что он ожидает, что метод будет возвращен)

Попробуй позвонить object.__getattr__ вместо этого это сработает:

class MyTest:
    def __init__(self):
         pass

    def __getattr__(self, attr):
        print(attr)   # so we see why getattr is called
        return object.__getattr__(self,attr)  # so it doesn't crash (neither it is useful...)

t = MyTest()
print ('my test object: %r' %t)

печатает:

__repr__
my test object: <__main__.MyTest instance at 0x00000000031B3808>

это специфическая проблема объекта Python 2/ старого стиля. Python 3 или объекты нового стиля не ведут себя одинаково

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