【问题标题】:Change a global variable that is used as default argument in a decorator in python更改在 python 的装饰器中用作默认参数的全局变量
【发布时间】:2020-08-05 10:14:15
【问题描述】:

我有一个函数使用全局 x 变量作为其参数的默认值。但是,在if __name__ == '__main__': 块中调用的main() 函数中,我希望允许用户更改此变量以修改函数的行为。这似乎不起作用,因为当我在函数中运行脚本 x 在这种情况下已经被 9 占用并且没有更改为 10,因为我想要它。

x = 9

def function(var=x):
    print(var)

def main():
    global x
    x = 10
    function()

if __name__ == '__main__':
    main()

main() 中简单地调用function(var=x) 在我的情况下是行不通的,因为function() 实际上是一个装饰器,而是:

from module import decorator

x = 9

@decorator(var1=x, var2=15)
def function():
    pass

def main():
    global x
    x = 10
    function()

if __name__ == '__main__':
    main()

另外,装饰器是从另一个模块导入的,所以我认为Defining a default argument as a global variable 中发布的答案可能也不起作用。更复杂的是,装饰器也应该接受其他参数,因此使用下面 kederrac 建议的类也不起作用。但是,由于这个模块是我自己制作的,我可以将必要的代码复制到我的新脚本中,但如果可能的话,我想避免这种情况。

非常感谢您的帮助!

【问题讨论】:

    标签: python python-3.x decorator global


    【解决方案1】:

    您可以使用class 作为装饰器

    class my_decorator:
        x = 9
    
        def __init__(self, f):
            self.f = f
    
        def __call__(self, *args, **kwargs):
                print(my_decorator.x)
                return self.f(*args, **kwargs)
    
    
    
    @my_decorator
    def function():
        pass
    
    def main():
        my_decorator.x = 4
        function()
    
    main()
    
    # 4
    

    如果您想将参数传递给您的装饰器,您可以使用:

    from functools import partial
    
    class my_decorator:
        x = 9
    
        def __init__(self, f, param1):
            self.f = f
            self.param1 = param1
    
        def __call__(self, *args, **kwargs):
                print(my_decorator.x)
                print(self.param1)
                return self.f(*args, **kwargs)
    
    
    
    @partial(my_decorator, param1='test_param')
    def function():
        pass
    
    def main():
        my_decorator.x = 4
        function()
    
    main()
    
    # 4
    # test_param
    

    【讨论】:

      【解决方案2】:

      我在 Beazley 和 Jones 撰写的 Python Cookbook 第 9.5 章中找到了解决方案。它们引入了能够通过使用nonlocal 来更改变量的访问器函数。我附上了一个示例解决方案。

      from functools import wraps, partial
      
      def attach_wrapper(obj, func=None):
          if func is None:
              return partial(attach_wrapper, obj)
          setattr(obj, func.__name__, func)
          return func
      
      def decorator(arg1, arg2):
          def decorate(func):
              @wraps(func)
              def wrapper(*args, **kwarg):
                  ...
                  do_something_with_arg1_and_arg2
                  ...
                  return func(*args, **kwargs)            
      
              @attach_wrapper(wrapper)
              def set_arg1(new_arg1):
                  nonlocal arg1
                  arg1 = new_arg1
              return wrapper
          return decorate
      

      所以现在您可以使用decorator 装饰您的function(),然后使用set_arg1() 作为function() 的方法更改arg1,如下所示:

      @decorator(arg1=9, arg2=15)
      def function():
          pass
      
      function.set_arg1(10)
      

      唯一的问题是,在这种情况下,我需要为我应用装饰器的所有函数调用此方法,所以也不理想。

      【讨论】:

        猜你喜欢
        • 2023-04-04
        • 1970-01-01
        • 1970-01-01
        • 2012-08-18
        • 2016-09-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多