Почему объект с переопределенным __getattr __ () выбрасывает TypeError? - PullRequest
0 голосов
/ 01 марта 2019

Вот код

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 ]

0 голосов
/ 01 марта 2019

В классе старого стиля, __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__.

0 голосов
/ 01 марта 2019

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

Поскольку вы насильственно зарезали этот метод, онвозвращает None и python пытается вызвать 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 или объекты нового стиля не ведут себя одинаково

...