使用函数或类在Python中实现装饰器时的不同行为
我想编写一个应用于类的方法的装饰器。装饰器应该维护一个状态,因此我想用一个类来实现它。但是,当存在嵌套调用时,类装饰器将失败,而带有函数的装饰器生成将工作 下面是一个简单的例子:使用函数或类在Python中实现装饰器时的不同行为,python,decorator,Python,Decorator,我想编写一个应用于类的方法的装饰器。装饰器应该维护一个状态,因此我想用一个类来实现它。但是,当存在嵌套调用时,类装饰器将失败,而带有函数的装饰器生成将工作 下面是一个简单的例子: def decorator(method): def inner(ref, *args, **kwargs): print(f'do something with {method.__name__} from class {ref.__class__}') return meth
def decorator(method):
def inner(ref, *args, **kwargs):
print(f'do something with {method.__name__} from class {ref.__class__}')
return method(ref, *args, **kwargs)
return inner
class class_decorator:
def __init__(self, method):
self.method = method
def __call__(self, *args, **kwargs):
print('before')
result = self.method(*args, **kwargs)
print('after')
return result
class test:
#@decorator
@class_decorator
def pip(self, a):
return a + 1
#@decorator
@class_decorator
def pop(self, a):
result = a + self.pip(a)
return result
t = test()
print(f'result pip : {t.pip(3)}')
print(f'result pop : {t.pop(3)}')
这将适用于'decorator'函数,但不适用于类\u decorator,因为'pop'方法中的嵌套调用只是“语法糖”。
类装饰器的问题是,self
不再作为第一个参数传递
我们想要的是模仿decorator
的行为,在这种行为中,我们返回一个不再需要self
传递给它的方法
这可以通过使用partial
函数直接完成,方法是将其设置为
您会注意到,调用的第一个函数是\uuu get\uu
class class_decorator:
def __init__(self, method):
self.method = method
def __set_name__(self, owner, name):
self.owner = owner
def __call__(self, *args, **kwargs):
print('before')
result = self.method(*args,**kwargs)
print('after')
return result
def __get__(self, instance, owner):
print('calling get')
from functools import partial
return partial(self, instance)
您面临的问题是,类方法的修饰符不是传递的方法,而是函数 在Python中,方法和函数是两种不同的类型:
Python 3.8.3 (default, May 17 2020, 18:15:42)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.15.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: class X:
...: def m(self, *args, **kwargs):
...: return [self, args, kwargs]
In [2]: type(X.m)
Out[2]: function
In [3]: type(X().m)
Out[3]: method
In [4]: X.m(1,2,x=3)
Out[4]: [1, (2,), {'x': 3}]
In [5]: X().m(1,2,x=3)
Out[5]: [<__main__.X at 0x7f1424f33a00>, (1, 2), {'x': 3}]
在上面我使用了一个简单的列表state
,但是您可以使用任意多的state,包括类实例。然而,重要的一点是装饰器返回的是函数
对象。这样,当在类实例中查找时,Python运行时将构建适当的方法
对象,以使方法调用工作
另一个非常重要的考虑点是,装饰器在类定义时间(即类对象建立时)执行,而不是在实例创建中执行。这意味着您将在decorator中拥有的状态将在类的所有实例之间共享
另一个可能不明显的事实是,在过去一直困扰着我的是,像
\uuu调用\uuu
或\uu添加\uu
这样的特殊方法没有首先在实例中查找,Python直接在类对象中查找它们。这是一个有文档记录的实现选择,但仍然是一个“奇怪”的不对称,可能会令人惊讶。您可以只做部分(自我、实例)
,而不是部分(自我、实例)
。修复了我的答案。谢谢
In [1]: def deco(f):
...: state = [0]
...: def decorated(*args, **kwargs):
...: state[0] += 1
...: print(state[0], ": decorated called with", args, **kwargs)
...: res = f(*args, **kwargs)
...: print("return value", res)
...: return res
...: return decorated
In [2]: class X:
...: def __init__(self, x):
...: self.x = x
...: @deco
...: def a(self):
...: return self.x + 1
...: @deco
...: def b(self):
...: return 10 + self.a()
In [3]: x = X(12)
In [4]: x.a()
1 : decorated called with (<__main__.X object at 0x7f30a76f41c0>,)
return value 13
Out[4]: 13
In [5]: x.a()
2 : decorated called with (<__main__.X object at 0x7f30a76f41c0>,)
return value 13
Out[5]: 13
In [6]: x.b()
1 : decorated called with (<__main__.X object at 0x7f30a76f41c0>,)
3 : decorated called with (<__main__.X object at 0x7f30a76f41c0>,)
return value 13
return value 23
Out[6]: 23