python,装饰和封闭混淆

python, confused in decorate and closure

我有一些测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
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"的闭包?

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

思考。


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

1
2
3
4
5
def f():
    g = 2
    def f2():
        print g
    f2()

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

一般来说,没有一种方法可以满足您的需要,即从函数外部在函数中设置任意变量。最接近的是,您可以在装饰器中使用global inputed_numinputed_num指定为全局变量。那么,test将获得全球价值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

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


My question is: In wrap function, is there not a closure that func can got 'inputed_num' ?

对不起,这不是装饰工的工作方式。它们在最初定义函数之后被应用。到那时,已经太晚了。

当你写作时:

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

相当于:

1
2
3
4
def test(a):
    return a + inputed_num

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

为了实现您的目标,让inputed_num成为第一个要测试的参数。然后,让你的修饰符传递这个参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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

希望能为你清理一切


正如@raymond所说的那样-在定义函数后应用decorator。这意味着,在编译函数体本身时,pythn会看到inputed_num变量,当我检测到一个本地定义的变量时,它会生成代码来尝试将a作为全局变量访问。

这意味着你可以在你的装饰师那里为它做些工作:您的修饰器可以在函数globals()空间中设置一个具有所需名称的全局变量,然后调用该函数。它应该在单线程代码中可靠地工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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

还有:

1
2
>>> print test(1)
6


这不是装修工应该用的方式,我想你的目的可以用这个来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def outwrapper(n):
    def wrapper(func):
        def f(*args, **argw):
            return n + func(*args, **argw)
        return f
    return wrapper


@outwrapper(4)
def test(n):
    return n


print test(1)