【问题标题】:Python decorator does not recognize global variablePython 装饰器无法识别全局变量
【发布时间】:2021-05-06 14:39:07
【问题描述】:

我刚刚编码的问题的 mwe:

from decorator import decorator


@decorator
def deco(func, deco_name=None, *args, **kwargs):
    print(f"DECORATOR {deco_name} APPLIED")
    return func(*args, **kwargs)


deco_name = None


class Greeter:
    def __init__(self, _deco_name):
        global deco_name
        deco_name = _deco_name

    @deco(deco_name=deco_name)
    def hello(self, name):
        print(f"Hello {name} :)")
        print(deco_name)


g = Greeter("MYDECO")
g.hello("Yoshi")

控制台输出:

DECORATOR None APPLIED
Hello Yoshi :)
MYDECO

我的项目中有类似的设置,但我不明白为什么装饰器函数 deco() 不知道全局变量 deco_name 的更新值的值(它打印 DECORATOR None APPLIED 而不是 DECORATOR MYDECO APPLIED )。 装饰函数 hello() 确实知道最后一个打印语句生成的 MYDECO 所看到的更新值。 我需要一些方法来在运行时设置一个全局变量并将其传递给装饰器,如果有人可以 a) 向我解释为什么我的方法是错误的并且 b) 给我一个修复/替代解决方案,我会很高兴。

提前致谢。

【问题讨论】:

  • 打印语句的顺序应该是胶水。装饰器的代码在类创建时被调用,在实例之前和__init__()之前。

标签: python global-variables decorator


【解决方案1】:

主要问题是装饰器是在创建类时创建的。所以在设置变量之前调用外部装饰器。您可以将所有这些放在包装函数中,这样它只会在调用 hello() 时被调用。

我不确定您为什么在这里使用全局变量。您可以在包装函数中访问实例变量,这对我来说更有意义。这是一个设置和访问全局变量和实例变量的示例。也许它会为事情指明一个有用的方向:

def deco(f):
    def wrapper(*args):
        instance = args[0]
        print(f"DECORATOR Instance {instance.deco_name} APPLIED")
        print(f"GLOBAL NAME {global_deco_name} APPLIED")
        f(*args)
    return wrapper

global_deco_name = "global_deco"

class Greeter:
    def __init__(self, _deco_name):
        global global_deco_name
        self.deco_name = _deco_name
        global_deco_name = _deco_name

    @deco
    def hello(self, name):
        print(f"Hello {name} :)")
        print(self.deco_name)
        print(global_deco_name)
        


g = Greeter("MYDECO")
g.hello("Yoshi")

打印

DECORATOR Instance MYDECO APPLIED
GLOBAL NAME MYDECO APPLIED
Hello Yoshi :)
MYDECO
MYDECO

【讨论】:

  • 是的,mwe 没有明确目的。我不能直接共享我的项目代码,而且与问题无关的代码也太多了,但我基本上在一个单独的模块中导入了一个 PySpark 日志框架,该模块提供了适用于每个功能的装饰器。对于大多数模块来说,它可以工作,但对于一个模块来说,它变得很棘手,因为一个类被初始化,它在运行时获得了日志装饰器所需的密钥:D 好吧,仍然感谢,现在我至少理解了这个问题并可以寻找另一种解决方案。
【解决方案2】:

我很确定这里发生的情况是 deco_name 在对象初始化之前被解释。当你在一个类中注释一个函数时,几乎会发生这种情况,一旦类本身被解释,装饰器就“准备好了”,这意味着在你分配 g 时@deco 是不可更改的。

最好的选择可能是将 hello 打包到一个不同的函数中,以实现装饰器的功能:

def hello(self, name):
  return deco(self._hello, deco_name=self.deco_name, name)
def _hello(self, name):
  # your hello

这也将全局 deco_name 替换为类属性。

【讨论】:

  • 谢谢。我认为它是这样的,但起初似乎真的违反直觉。遗憾的是,该解决方案并不适用,因为在我的项目中,我在一个类中有很多函数,我必须用日志装饰器进行注释,因此为每个函数创建辅助函数会使代码过多。好吧,那我明天就得另找办法了。
猜你喜欢
  • 1970-01-01
  • 2018-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-23
  • 2021-10-22
  • 1970-01-01
  • 2017-03-27
相关资源
最近更新 更多