【问题标题】:Python "Multi-Level Decorator" - how does this work?Python“多级装饰器” - 这是如何工作的?
【发布时间】:2016-01-24 14:27:13
【问题描述】:

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

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

函数装饰器:

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. 谁能进一步了解内部发生的情况?

【问题讨论】:

    标签: python decorator python-decorators


    【解决方案1】:

    你已经创建了一个装饰器工厂;产生装饰器的可调用对象。在这种情况下,当使用globaldec 类作为装饰器时,您忽略globaldec.__init__()(原始funcdec 类对象)的func 参数。您改为将其替换为 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 将其用作装饰器时,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)
    

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

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

    【讨论】:

      猜你喜欢
      • 2011-07-25
      • 2020-10-15
      • 2016-01-06
      • 1970-01-01
      • 2020-04-08
      • 2014-04-14
      • 2018-06-11
      • 2017-09-12
      • 1970-01-01
      相关资源
      最近更新 更多