Python"多层装饰器" - 这是如何运作的?

时间:2015-10-25 12:27:40

标签: python decorator python-decorators

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

一个精简的例子如下所示。

函数装饰器:

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. 任何人都可以对内部发生的事情有所了解吗?

1 个答案:

答案 0 :(得分:3)

您已创建装饰工厂;一个可调用的对象,用于生成装饰器。在这种情况下,当使用func类作为装饰器时,您忽略<{1}} globaldec.__init__() funcdec参数globaldec(原始globaldec类对象)。您改为将其替换为function1类的实例,然后将其用作function2@globaldec的真正装饰器。

那是因为装饰者只是语法糖;应用于class funcdec:行的class funcdec(object): pass funcdec = globaldec(funcdec) 装饰器可以表示如下:

funcdec

所以globaldec该类被func的实例取代。

我没有使用类,而是使用函数;像stateimport random def funcdec(func): state = random.random() def wrapper(*args, **kwargs): return func(state, *args, **kwargs) return wrapper 这样的状态会成为封闭。

然后您的原始装饰者可以这样写:

funcdec()

因此,当Python将其应用为装饰器时,wrapper将返回function1函数,替换原始的function2wrapper()函数将替换为该函数对象。然后调用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现在将package main //#cgo pkg-config: gtk+-3.0 //#include "ui.h" import "C" func CInit() { C.Init(nil, 0) } func CMain() { C.Main() } func CShowWindow() { C.ShowWindow() } func main() { CInit() CShowWindow() CMain() } 称为共享值。