python,在装饰和关闭时感到困惑

我有一些测试代码:

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'没有被定义

我的问题是:在包装函数中,是否没有func可以得到''inputed_num'的闭包?

无论如何,如果不是,我该怎么做才能得到我的目标: 初始化一些值,并直接在主函数中使用这个值。

想。


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

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

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

一般来说,没有办法做到你想做的事情,即在函数外部设置一个函数中的任意变量。 最接近的是,你可以在装饰器中使用global inputed_num来将inputed_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

但是当然,变量设置是全局的,所以多个并发使用(例如,递归)将不起作用。 (只是为了好玩,你也可以在这里看到一个非常神秘的方式来做到这一点,但它太疯狂了,不适合在真实世界中使用。)


我的问题是:在包装函数中,是否没有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

希望能为你解决一切问题:-)


正如@雷蒙德所说的那样 - 在定义函数之后应用装饰器。 这意味着在编译函数体本身时,Pythn会看到inputed_num变量,并且当它伪装一个本地定义的变量时,它会生成代码来尝试将其作为全局变量来访问它。

这意味着你可以在你的装饰器中为它做一个解决方法:你的装饰器可以在函数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
链接地址: http://www.djcxy.com/p/28563.html

上一篇: python, confused in decorate and closure

下一篇: argparse: change argument from positional to optional in certain cases