我需要为类方法创建包装器,以便在调用特定方法之前和/或之后执行。
这是一个最小的例子:
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')
这会在getattr(self, name)()
行显示错误:
TypeError:' NoneType'对象不可调用
如果我在类方法之前注释掉装饰器,它可以完美地运行:
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')
输出结果为:
执行功能:my_function
我的功能在这里被调用。
装饰器本身与教科书示例相同。当使用getattr
在Python中调用装饰方法时,看起来在低级别出现问题。
您对如何修复此代码有任何想法吗?
答案 0 :(得分:2)
这与getattr()
无关。当您尝试直接调用my_function()
时,会收到完全相同的错误:
>>> engine.my_function()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
你有两个问题:
您的装饰者永远不会返回wrapper
,因此会返回None
。此返回值将替换my_function
,并且是导致错误的直接原因; MyClass.my_function
设置为None
:
>>> MyClass.my_function is None
True
您的包装器不带任何参数,包括self
。一旦你正确地退回它,你就需要它才能工作。
通过取消缩进return wrapper
行来解决第一个问题;它目前是wrapper
函数本身的一部分,应该成为my_decorator
的一部分:
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
答案 1 :(得分:1)
您的问题只是部分回答。以下是如何修改wrapper
(以及call()
)方法,以便它们接受其他参数 - 这将使其完全有效(以及Python 2和3):
class MyClass:
def call(self, name, *args, **kwargs):
print("Executing function: {!r}".format(name))
getattr(self, name)(*args, **kwargs)
def my_decorator(some_function):
def wrapper(self, *args, **kwargs):
print("Before we call the function.")
retval = some_function(self, *args, **kwargs)
print("After we call the function.")
return retval
return wrapper
@my_decorator
def my_function(self):
print("My function is called here.")
del my_decorator # Not a permanent part of class.
engine = MyClass()
engine.call('my_function')
输出:
Executing function: 'my_function'
Before we call the function.
My function is called here.
After we call the function.