python闭包/嵌套函数在分配给外部数组时失败

时间:2018-09-19 17:40:05

标签: python closures

如果符号是python函数的关闭似乎有问题 引用的对象已分配:

def outer():
    p = []
    def gen():
        def touch(e):
            if e[0] == 'add':
                p.append(e);
            elif e[0] == 'rem':
                p = [ x for x in p if not (x[1] == e[1]) ]
        return touch
    f = gen()
    for i in [["add","test1"],["add","test2"],["rem","test2"],["rem","test1"]]:
        f(i)      

outer();

结果是:

Traceback (most recent call last):
  File "b.py", line 22, in <module>
    outer();
  File "b.py", line 20, in outer
    f(i)      
  File "b.py", line 14, in touch
    p.append(e);
UnboundLocalError: local variable 'p' referenced before assignment

如果我只是为了测试,请替换:

 -       p = [ x for x in p if not (x[1] == e[1]logig is) ]                                                                                                                                
 +       a = [ x for x in p if not (x[1] == e[1]) ]                                                                                                                                

错误消失了,但是代码不是我想要的。 python闭包/嵌套函数是否有上述行为?我是否需要包装数组以在对象内部进行修改并仅调用函数?

另一方面,这有效:

class o():
    def __init__(self):
        self.p = []
    def add(self,e):
        self.p.append(e);
    def rem(self,e):
        self.p = [ x for x in self.p if not (x[1] == e[1]) ]

def outer():
    p = o()
    def gen():
        def touch(e):
            if e[0] == 'add':
                p.add(e);
            elif e[0] == 'rem':
                p.rem(e)
        return touch
    f = gen()
    for i in [["add","test1"],["add","test2"],["rem","test2"],["rem","test1"]]:
        f(i)      

outer();

1 个答案:

答案 0 :(得分:2)

因为要在p内分配touch,所以它成为touch中的局部变量,并有效地“隐藏”了所有其他名称p。为了告诉Python您实际上要引用p中的outer,应使用nonlocal p,例如:

def outer():
    p = []
    def touch(e):
        # The following line is required to refer to outer's p
        nonlocal p
        if e[0] == 'add':
            p.append(e)
        elif e[0] == 'rem':
            p = [ x for x in p if not (x[1] == e[1]) ]
    for i in [["add","test1"],["add","test2"],["rem","test2"],["rem","test1"]]:
        touch(i)
outer()

您的第二个示例之所以有效,是因为在两种p情况下都引用了touch的属性,而不是进行赋值(p = ...)。

请参见nonlocal in the Python reference documentationscopes的参考文档和PEP 3104,其中提出了nonlocal语法。 nonlocal仅存在于Python 3中,但如果需要使用Python 2,则存在there is a workaround