当前位置: 首页 > 知识库问答 >
问题:

蟒蛇,在装饰和关闭中混淆

沙靖琪
2023-03-14

我有一些测试代码:

def num(num):
    def deco(func):
        def wrap(*args, **kwargs):
            inputed_num = num
            return func(*args, **kwargs)
        return wrap
    return deco


@num(5)
def test(a):
    return a + inputed_num

print test(1)

运行此代码时,我收到一个错误,显示未定义“inputed_num”

我的问题是:在wrap函数中,func是否可以得到“inputed_num”的闭包?

无论如何,如果没有,我应该如何实现我的目标:初始化一些值,并在主函数中直接使用这个值。

思考。

共有3个答案

吕永嘉
2023-03-14

正如@Raymond所说的那样,定义函数后应用装饰器。这意味着,在编译函数体本身时,Pythn会看到inputed_num变量,当它不是本地定义的变量时,它会生成代码以尝试将其作为全局变量访问。

这意味着您可以在decorator中对其进行变通:decorator可以在函数globals()空间中使用所需的名称设置一个全局变量,然后调用该函数。它应在单线程代码中可靠工作:

def num(num):
    def deco(func):
        def wrap(*args, **kwargs):
            glob = func.func_globals
            marker = object()
            original_value = glob.get("inputed_num", marker)
            glob["inputed_num"] = num
            result = func(*args, **kwargs)
            if original_value is marker:
                del glob["inputed_num"]
            else:
                glob["inputed_num"] = original_value
            return result
        return wrap
    return deco


@num(5)
def test(a):
    return a + inputed_num

和:

>>> print test(1)
6
昝晗昱
2023-03-14

我的问题是:在wrap函数中,func是否可以得到“inputed_num”的闭包?

抱歉,这不是装修工的工作方式。它们在函数最初定义后得到应用。到那时,已经太晚了。

当你写:

@num(5)
def test(a):
    return a + inputed_num

这相当于:

def test(a):
    return a + inputed_num

test = num(5)(test)       # note that num(5) is called after test() is defined.

为了实现你的目标,让inputed_num成为第一个要测试的论点。然后,让你的装修师通过这个论点:

def num(num):
    def deco(func):
        def wrap(*args, **kwargs):
            inputed_num = num
            return func(inputed_num, *args, **kwargs)  # this line changed
        return wrap
    return deco

@num(5)
def test(inputed_num, a):                              # this line changed
    return a + inputed_num

@num(6)
def test2(inputed_num, a):
    return a + inputed_num

print test(10)   # outputs 15
print test2(10)  # outputs 16

希望能为你扫清一切:-)

马野
2023-03-14

不,没有那样的结局。函数可以关闭周围词法上下文中的变量,而不是调用上下文中的变量。换句话说,如果您实际在另一个函数中写入一个函数,那么内部函数可以访问外部函数中的变量:

def f():
    g = 2
    def f2():
        print g
    f2()

但是函数永远不能访问调用它们的函数内部的变量。

一般来说,没有办法做你想做的事情,即在函数外部设置任意变量。最接近的是,您可以在装饰器中使用全局inputed_numinputed_num分配为全局变量。然后test将访问全局值。

def num(num):
    def deco(func):
        def wrap(*args, **kwargs):
            global outsider
            outsider = num
            return func(*args, **kwargs)
        return wrap
    return deco
@num(5)
def test(a):
    print a+outsider

>>> test(2)
7

当然,变量设置是全局的,因此多个并发使用(例如递归)将不起作用。(只是为了好玩,你也可以在这里看到一个非常神秘的方法来做这件事,但它太疯狂了,在现实世界中没有用处。)

 类似资料:
  • 我安装了两个python3.8,其中一个在终端中具有sudo权限时使用,另一个在没有权限时使用: 安装了一个python3.8(我想根据我使用的软件): 和为spyder安装的python3.8 我需要anaconda3/bin/python3.8用于一个项目,但现在我想安装一个我必须自己构建的包到python3.8,我花了几天时间才意识到sudo哪个python3.8和哪个python3.8是不

  • 我有一个类有很多静态方法与龙卷风协程装饰。我想添加另一个装饰器,捕捉异常并将它们写入文件: 但是,它不适用于装饰器: 我知道,Tornado使用了的方法,这可能是基于异常的,也许它会以某种方式阻止其他装饰程序的尝试捕获。。。那么,我如何使用我的装饰器来处理Tornado协同程序的异常呢? 多亏了Martijn Pieters,我的代码得以运行: 所以,我只需要指定Tornado。我试图将装饰器添加

  • 我正在运行Ubuntu 18.04。 我使用mysql连接器-python连接Python到MySQL。 我使用的是Python 3.6.7,并且已经安装了mysql连接器-python。 我已经安装了mysql连接器-python-py3_8.0.13-1ubuntu18.10_all.deb. 在运行Python脚本时,mysql。连接器模块似乎加载正确,但脚本在碰到光标时失败。next()具

  • 假设我有一些资源,我想在用python编写的aws lambda中的不同请求之间共享。我应该如何实现这一点? 是否有“启动后”挂钩,或者我应该在第一次调用时惰性地创建资源?“延迟初始化”的缺点是,它意味着一些请求会随机变慢,因为您选择了一个消费者来承担启动成本。 此外…这些资源会在lambda可执行文件被“冻结”后幸存下来吗? 本页https://docs.aws.amazon.com/lambd

  • 我需要在我的中添加一个新的目录位置,但问题是我使用的是一个全新安装的系统(Linux),其中尚未定义任何。我读过并使用过,我认为我很了解它,但我不知道当没有存在时会发生什么。 我不能附加到不存在的东西上,但我希望当前发现的所有重要库都能正常工作,因此要小心,我在Python中使用了来获取所有标准值。然后我为定义了一个-变量,包括我刚刚找到的所有节点,以及我的新目录。但是哇,很多东西都停止工作了!P

  • 尝试,但它给我一个错误。与这里的许多其他问题类似,但提供的解决方案在我安装后无法解决错误。 我的文件 我正在使用Windows 7,蟒蛇3.4(我听说与安装MySQL-python不兼容,所以我下载了mysql客户端1.3.6)。 当我运行时,它会导致 同样,它在运行时要求配置win.h,以及一个可怕的错误 我对此很困惑。我一直在看的教程偏离了我这里,我不知道如何处理mysqlclient的WHL