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

Python "Multi-Level Decorator" - how does this work?

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

下面显示了一个简化的示例。

函数装饰器:

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

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

装饰函数装饰器:

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 只为函数装饰器的所有实例初始化一次。

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

问题:

  1. 这个技术有名字吗?
  2. 任何人都可以进一步说明内部发生的事情吗?

您已经创建了一个装饰器工厂;生成装饰器的可调用对象。在这种情况下,当使用 globaldec class 作为装饰器。您将其替换为 globaldec class 的实例,然后将其用作 function1function2.

的真正装饰器

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

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

所以 funcdec class 被 globaldec 的实例代替。

我不使用 classes,而是使用函数; funcstate 等状态变为闭包。

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

import random

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

所以当Python将其作为装饰器应用时,funcdec() returns 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)

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

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 作为共享值。