Reputation: 33
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
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