Почему объект с переопределенным __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 или объекты нового стиля не ведут себя одинаково