Joseph Samela
Joseph Samela

Reputation: 33

Python __getattr__ 'NoneType' object is not callable

For the class Thing, when I call an undefined method such as .doamethod()...

class Thing:
    def __init__(self):
        pass
    def __getattr__(self, method):
        print('Run method: '+method)

t = Thing()
t.doamethod()

...I get this output:

Run method: doamethod
Traceback (most recent call last):
  File "C:\Users\person\classtest.py", line 9, in <module>
    t.doamethod()
TypeError: 'NoneType' object is not callable

Since the text Run method: doamethod was printed I know the contents of __getattr__ ran (which is good, I want this) but it also raised TypeError: 'NoneType' object is not callable. Why?

Upvotes: 3

Views: 1233

Answers (1)

Samwise
Samwise

Reputation: 71454

__getattr__ returns the attribute. Your implementation of __getattr__ returns None -- so when you say t.doamethod, the value of that is None, and when you try to call it with (), you get the not callable error.

If you want your attribute to be a callable no-op, you could do:

class Thing:
    # note: no need to add an empty __init__ method here
    def __getattr__(self, method):
        def impl(*args, **kwargs):
            return None
        print(f'Run method: {method}')
        return impl

t = Thing()
t.doamethod    # prints "Run method: doamethod"
t.doamethod()  # prints "Run method: doamethod"

If you want the attribute to be a callable that prints "Run method" when it's called (rather than when the method is accessed), then put that code inside the function that __getattr__ returns:

class Thing:
    def __getattr__(self, attr):
        def impl(*args, **kwargs):
            print(f'Run method: {attr}({args}, {kwargs})')
        print(f'Get attribute: {attr}')
        return impl

t = Thing()
func = t.foo          # prints "Get attribute: foo"
func()                # prints "Run method: foo((), {})"
func(42, arg2="bar")  # prints "Run method: foo((42,), {'arg2': 'bar'})"

Upvotes: 4

Related Questions