当前位置: 首页 > 面试题库 >

如何使用装饰器将变量注入作用域?

仲孙善
2023-03-14
问题内容

[免责声明:可能会有更多pythonic方法来做我想做的事,但我想知道python的作用域在这里是如何工作的]

我试图找到一种方法来制作装饰器,该装饰器的作用类似于将名称注入另一个函数的作用域(以使名称不会泄漏到装饰器作用域之外)。例如,如果我有一个函数说要打印一个var尚未定义的名为的变量,我想在调用它的装饰器中定义它。这是一个中断的示例:

c = 'Message'

def decorator_factory(value):
    def msg_decorator(f):
        def inner_dec(*args, **kwargs):
            var = value
            res = f(*args, **kwargs)
            return res
        return inner_dec
    return msg_decorator

@decorator_factory(c)
def msg_printer():
    print var

msg_printer()

我希望它打印“ Message”,但它给出:

NameError: global name 'var' is not defined

追溯甚至指向whlevar定义:

<ipython-input-25-34b84bee70dc> in inner_dec(*args, **kwargs)
      8         def inner_dec(*args, **kwargs):
      9             var = value
---> 10             res = f(*args, **kwargs)
     11             return res
     12         return inner_dec

所以我不明白为什么找不到它var

有什么办法可以做这样的事情吗?


问题答案:

你不能 作用域名称(闭包)在编译时确定,不能在运行时添加更多。

您可能希望实现的最好方法是使用函数 自己的 全局名称空间添加 全局 名称: __

def decorator_factory(value):
    def msg_decorator(f):
        def inner_dec(*args, **kwargs):
            g = f.__globals__  # use f.func_globals for py < 2.6
            sentinel = object()

            oldvalue = g.get('var', sentinel)
            g['var'] = value

            try:
                res = f(*args, **kwargs)
            finally:
                if oldvalue is sentinel:
                    del g['var']
                else:
                    g['var'] = oldvalue

            return res
        return inner_dec
    return msg_decorator

f.__globals__是包装函数的全局名称空间,因此即使装饰器位于其他模块中也可以使用。如果var已经定义为全局变量,则将其替换为新值,并在调用函数后恢复全局变量。

之所以可行,是因为在函数中任何未分配给且在周围范围中找不到的名称都被标记为全局名称。

演示:

>>> c = 'Message'
>>> @decorator_factory(c)
... def msg_printer():
...     print var
... 
>>> msg_printer()
Message
>>> 'var' in globals()
False

但是除了修饰,我也可以 直接var在全局范围内进行定义。 __

请注意,更改全局变量不是线程安全的,并且对同一模块中其他函数的任何瞬时调用也仍将看到该相同的全局变量。



 类似资料:
  • 我有一个特殊的用例,需要在html端呈现图像的完整url。前任;Facebook Open Graph需要完整图像url才能正常工作,相对图像或绝对路径无法正常工作。 我目前正在使用@vue/cli和typescript。我有以下组成部分: 上面的渲染很好,它会生成一个img标记,结果如下: 但假设我的领域是,以下是我想要的结果: 我不想在模板中硬编码任何东西。我想使用一个环境变量,这样我就可以在

  • 在MobX 中使用 ES.next 装饰器是可选的。本章节将解释如何(避免)使用它们。 使用装饰器的优势: 样板文件最小化,声明式代码。 易于使用和阅读。大多数 MobX 用户都在使用。 使用装饰器的劣势: ES.next 2阶段特性。 需要设置和编译,目前只有 Babel/Typescript 编译器支持。 启用装饰器 如果想使用装饰器,请按照以下步骤操作。 TypeScript 启用 tsco

  • 问题内容: 在Python中的lambda函数上使用装饰器有什么语法?例: 结果如下: 但是,当我尝试使用lambda进行相同操作时: 我得到这个: 我觉得这可能是通过允许将语句“插入” lambda使其更通用的好方法。但是,如果存在这样的功能,我不知道语法是什么。 问题答案:

  • 问题内容: 我正在尝试将JavaScript变量作为PHP变量包含在PHP代码中,但是这样做有问题。单击按钮时,将调用以下功能: 可能吗? 问题答案: PHP在服务器端运行。JavaScript在请求页面的用户的浏览器中在客户端运行。到执行JavaScript时,服务器上都无法访问PHP。请阅读本文,了解有关客户端和服务器端编码的详细信息。 简而言之,这是什么: 您在办公桌下方的计算机上的浏览器中

  • 问题内容: 我很好奇AngularJS中的装饰器到底是什么。除了AngularJS文档中的简短内容和youtube视频中的简短提及(尽管很有趣)之外,装饰者在线上没有太多信息。 正如Angular所说的那样,装饰器是: 装饰服务,允许装饰者截取服务实例的创建。返回的实例可以是原始实例,也可以是委派给原始实例的新实例。 我真的不知道这 意味着 什么,而且我不确定为什么要将这种逻辑与服务本身分开。例如

  • 问题内容: 考虑这个小例子: 哪个打印 为什么参数(应该是Test obj实例)没有作为第一个参数传递给装饰函数? 如果我手动进行操作,例如: 它按预期工作。但是,如果我必须事先知道某个函数是否装饰,它就破坏了装饰器的全部目的。这里的模式是什么,还是我误会了什么? 问题答案: tl; dr 您可以通过将类作为描述符并返回部分应用的函数来解决此问题,该函数从中应用对象作为参数之一,如下所示 实际问题