14

我刚刚学习了python@decorator,很酷,但是很快我发现我修改后的代码出现了奇怪的问题。

def with_wrapper(param1):
    def dummy_wrapper(fn):
        print param1
        param1 = 'new'
        fn(param1)
    return dummy_wrapper

def dummy():
    @with_wrapper('param1')
    def implementation(param2):
        print param2

dummy()

我调试它,它在 print param1 抛出异常

UnboundLocalError: local variable 'param1' referenced before assignment

如果我删除 param1 = 'new'此行,而不对外部范围的变量进行任何修改操作(链接到新对象),则此例程可能会起作用。

这是否意味着我只复制了一份外部范围变量,然后进行修改?


谢谢德尔南,关闭是必不可少的。可能来自这里的答案: 与语言 X 闭包相比,Python 中的闭包有哪些限制?

类似的代码如下:

def e(a):
    def f():
        print a
        a = '1'
    f()
e('2')

而且这似乎是以前烦人的全局变量:

a = '1'
def b():
    #global a
    print a
    a = '2'
b()

这是通过添加全局符号修复的。但是对于关闭,没有找到这样的符号。感谢 unutbu,Python 3 给了我们nonlocal

我从上面知道直接访问外部变量是只读的。但是看到之前的读取变量(print var)也受到影响,这有点不舒服。

4

3 回答 3

19

当 Python 解析一个函数时,它会在发现赋值左侧使用的变量时记录下来,例如

param1 = 'new'

它假定所有这些变量都是函数的局部变量。所以当你在这个任务之前

print param1

发生错误是因为 Python 在执行 print 语句时没有此局部变量的值。


在 Python3 中,您可以通过声明它param1是非本地的来解决此问题:

def with_wrapper(param1):
    def dummy_wrapper(fn):
        nonlocal param1
        print param1
        param1 = 'new'
        fn(param1)
    return dummy_wrapper

在 Python2 中,您必须使用一个技巧,例如在列表(或其他一些可变对象)中传递 param1:

def with_wrapper(param1_list):
    def dummy_wrapper(fn):
        print param1_list[0]
        param1_list[0] = 'new'   # mutate the value inside the list
        fn(param1_list[0])
    return dummy_wrapper

def dummy():
    @with_wrapper(['param1'])   # <--- Note we pass a list here
    def implementation(param2):
        print param2
于 2012-08-29T16:11:40.550 回答
0

param1您在函数中分配,这会产生param1一个局部变量。但是,在您打印它时尚未分配它,因此您会收到错误消息。Python 不会回退到在外部范围内寻找变量。

于 2012-08-29T16:12:56.440 回答
0

对于临时情况或探索性代码很方便(但不是很好的做法)

如果您想在 Python 2.x 中从外部范围捕获变量,那么使用 global 也是一种选择(具有通常的附带条件)。

虽然以下将抛出(在 inner 中分配 outer1 使其成为局部的,因此在 if 条件中是无界的):

def outer():
    outer1 = 1
    def inner():
        if outer1 == 1:
            outer1 = 2
            print('attempted to accessed outer %d' % outer1)

这不会:

def outer():
    global outer1
    outer1 = 1
    def inner():
        global outer1
        if outer1 == 1:
            outer1 = 2
            print('accessed outer %d' % outer1)
于 2015-07-17T13:29:53.823 回答