将导入上下文变量传递给装饰器

时间:2015-11-03 16:41:36

标签: python django

假设您有一个配置类(如Django' settings.py样式设置),它在应用程序启动时设置。你加载它就好了。

from myapp import config

CONF = config.CONF

您希望将此配置中的某个变量用作装饰器参数,如此。

@decorators.mydecorator(run_timeout=CONF.timeout)
def run_stuff_once():
    # blah, blah, blah...

@decorators.mydecorator(interval=CONF.interval)
def run_stuff_periodically():
    # blah, blah, blah...

如果运行此命令,配置变量将在运行时加载,永远不会更改。即使不使用装饰器,我们也可以看到这种情况发生。

>>> class Config(object):
...     x = 5
...
>>> config = Config()
>>>
>>> def func(param=config.x):
...     print(param)
...
>>> func()
5
>>> config.x = 9
>>> func()
5

这会导致两个问题:

  1. 如果在包含此函数的文件之后加载了该​​配置变量的配置,则该函数将使用该变量的原始值。
  2. 无论导入顺序如何,一旦加载文件,就无法动态更改配置。
  3. 这可以解决吗?如果我在谈论"正常函数",我会采用类似于解决PyLint W0102警告的解决方案,即

    >>> def func2(param=None):
    ...     if param is None:
    ...         param = config.x
    ...     print(param)
    ...
    >>> func2()
    9
    >>> config.x = 5
    >>> func2()
    5
    

    但是,不可能为装饰器执行此操作,因为如上所示,传递给装饰器的值可能会更改。我对此案的解决方案感到难过。

1 个答案:

答案 0 :(得分:1)

这应该提供一个动态装饰器,它在运行时而不是在定义时

进行评估
class Config(object):
     x = 5
     y = 6

config = Config()
config2 = Config()

# takes object and atribute as arguments
def dynamic_dec(reference,atrib,kw=None):

    # gets function pointer to wraped function
    def dynamic_dec_functor(functor):

        # creates lamda function to revalueate config value
        def func_wrapper(ref_wrapper=(lambda ref=reference: getattr(ref,atrib))):

            #call original function with result of lambda
            if(kw):
                #store lambda result to keyword arg
                functor(**{kw:ref_wrapper()})
            else:
                #pass lambda result to positional arg
                functor(ref_wrapper())

        return func_wrapper

    return dynamic_dec_functor

@dynamic_dec(config,'x','param')
def func(param="default"):
    print(param)

@dynamic_dec(config2,'y')
def func2(param):
    print(param)

#time to test


print("\n call func and func2 with original config \n")

func()
func2()

print("\n update original config \n")

config.x = 9
config.y = 10

print("\n call func and func2 after config change \n")

func()
func2()

print("\n func2 did not change as it used a different config object \n")