Python "Multi-Level Decorator" - 这是如何工作的?



我一直在脚本中编写一些用于数据库操作的函数,并决定使用函数装饰器来处理数据库连接样板

如下所示:

函数修饰符:

import random
class funcdec(object):
    def __init__(self,func):
        self.state = random.random()
        self.func = func
    def __call__(self,*args,**kwargs):
        return self.func(self.state,*args,**kwargs)
@funcdec
def function1(state,arg1,**kwargs):
    print(state)
@funcdec
def function2(state,arg2,**kwargs):
    print(state)
function1(10)
function2(20)

这意味着我可以减少样板文件的数量,但是每个函数都有不同的状态对象。所以如果我运行这个,我会得到这样的东西:

python decf.py 
0.0513280070328
0.372581711374

我想实现一个方法,让所有的装饰函数共享这个状态,我想出了这个。

函数decorator:
import random
class globaldec(object):
    def __init__(self,func):
        self.state = random.random()
    def __call__(self,func,*args,**kwargs):
        def wrapped(*args,**kawrgs):
            return func(self.state,*args,**kwargs)
        return wrapped
@globaldec
class funcdec(object):
    pass
@funcdec
def function1(state,arg1,**kwargs):
    print(state)
@funcdec
def function2(state,arg2,**kwargs):
    print(state)
function1(10)
function2(20)

现在,当我运行这个,状态对象只创建一次,每个应用程序和状态是相同的所有修饰函数,例如:

python decg.py 
0.489779827086
0.489779827086

直观地说,这对我来说是有意义的,因为globaldec只对函数decorator的所有实例初始化一次。

然而,我对这里到底发生了什么有点模糊,而且funcdec对象似乎不再被初始化或调用。

<标题>问题:
  1. 这个技术有名字吗?
  2. 有人能透露更多关于内部发生的事情吗?

您已经创建了一个装饰器工厂;生成装饰器的可调用对象。在这种情况下,当使用globaldec类作为装饰器时,忽略 func参数到globaldec.__init__()(原始funcdec类对象)。而是用globaldec类的实例替换它,然后将其用作function1function2的真正装饰器。

这是因为修饰符只是语法糖;应用于class funcdec:行的@globaldec装饰符可以这样表示:

class funcdec(object):
    pass
funcdec = globaldec(funcdec)

所以funcdec类被globaldec的一个实例代替了。

我将使用函数而不是类;像funcstate这样的状态变成闭包。

你原来的装饰符可以这样写:

import random
def funcdec(func):
    state = random.random()    
    def wrapper(*args, **kwargs):
        return func(state, *args, **kwargs)
    return wrapper

所以当Python应用this作为装饰器时,funcdec()返回wrapper函数,替换原来的function1function2函数被该函数对象替换。调用wrapper(),然后通过func闭包调用原始函数对象。

globaldec版本只是增加了另一层;外部函数产生装饰器,将闭包移出一步:

import random
def globaldec():
    state = random.random()    
    def funcdec(func):
        def wrapper(*args, **kwargs):
            return func(state, *args, **kwargs)
        return wrapper
    return funcdec

只创建一次装饰器:

funcdec = globaldec()
@funcdec
def function1(state,arg1,**kwargs):
    print(state)
@funcdec
def function2(state,arg2,**kwargs):
    print(state)

另一种模式是将状态存储为全局(您可以直接在decorator函数上这样做:

import random
def funcdec(func):
    if not hasattr(funcdec, 'state'):
        # an attribute on a global function is also 'global':
        funcdec.state = random.random()
    def wrapper(*args, **kwargs):
        return func(funcdec.state, *args, **kwargs)
    return wrapper

现在您不再需要生成一个专用的装饰器对象,wrapper现在引用funcdec.state作为共享值。

最新更新