anverx
anverx

Reputation: 146

Why does an object with redefined __getattr__() throws TypeError?

Here is the code

class MyTest: 
    def __init__(self):
         pass

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

So print triggers a TypeError: 'NoneType' object is not callable while i only want to see if object exists. Granted this code isn't very useful. But i've had a stub class like that in a big code base so i did

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

and got a Type Error: 'NoneType' object is not callable but the line doesn't call anything! I guess python is calling some functions implicitly behind the scenes.

Curiously this doesn't happen if the class inherits from Object

What's going on?

Upvotes: 2

Views: 1086

Answers (2)

chepner
chepner

Reputation: 530970

In an old-style class, __getattr__ is used for a greater variety of attribute access, including magic methods. The % operator is trying to call t.__repr__() in order to fill in the %r placeholder, but t.__repr__ is evaluated by t.__getattr__('__repr__'), which returns None.

In the if case, a different magic method is invoked, but the same problem occurs.

>>> 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

Use a new-style class, __getattr__ is only invoked if an attribute cannot be found via the normal method (checking the __dict__ attribute of the instance or of any of the class's in the instance's 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

In the if f case, f itself doesn't implement __nonzero__ or __len__, and neither does its parent object, but in that case, no attribute is used; the fact that f is, in fact, an object is used. In f.x, x is found in the instance's attribute dict, so its value is returned directly. Only y, which isn't otherwise defined by f, Foo, or object, invokes a call to __getattr__.

Upvotes: 4

Jean-Fran&#231;ois Fabre
Jean-Fran&#231;ois Fabre

Reputation: 140168

In python 2, with old-style classes, when you try to call __repr__ (when printing) on an object, you have __getattr__ called.

Since you violently stubbed this method, it returns None and python tries to call None (because it's expecting a method to be returned)

Try to call object.__getattr__ instead, that'll work:

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)

prints:

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

this is a specific python 2/old-style object issue. Python 3 or new-style objects don't behave the same

Upvotes: 2

Related Questions