I need to make wrappers for class methods, to be executed before and/or after the call of a specific method.
Here is a minimal example:
class MyClass:
def call(self, name):
print "Executing function:", name
getattr(self, name)()
def my_decorator(some_function):
def wrapper():
print("Before we call the function.")
some_function()
print("After we call the function.")
return wrapper
@my_decorator
def my_function(self):
print "My function is called here."
engine = MyClass()
engine.call('my_function')
This gives me an error at the line getattr(self, name)()
:
TypeError: 'NoneType' object is not callable
If I comment out the decorator before the class method, it works perfectly:
class MyClass:
def call(self, name):
print "Executing function:", name
getattr(self, name)()
def my_decorator(some_function):
def wrapper():
print("Before we call the function.")
some_function()
print("After we call the function.")
return wrapper
# @my_decorator
def my_function(self):
print "My function is called here."
engine = MyClass()
engine.call('my_function')
The output is:
Executing function: my_function
My function is called here.
The decorator itself is identical to textbook examples. It looks like something goes wrong at a low level when calling a decorated method in Python with getattr
.
Do you have any ideas on how to fix this code?
This has nothing to do with getattr()
. You get the exact same error when you try to call my_function()
directly:
>>> engine.my_function()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
You have 2 problems:
Your decorator never returns the wrapper
, so None
is returned instead. This return value replaces my_function
and is the direct cause of your error; MyClass.my_function
is set to None
:
>>> MyClass.my_function is None
True
Your wrapper takes no arguments, including self
. You'll need this for it to work once you do return it properly.
The first problem is fixed by un-indenting the return wrapper
line; it is currently part of the wrapper
function itself, and should be part of my_decorator
instead:
def my_decorator(some_function):
def wrapper(self):
print("Before we call the function.")
# some_function is no longer bound, so pass in `self` explicitly
some_function(self)
print("After we call the function.")
# return the replacement function
return wrapper