【问题标题】:python closure/nested function fail when with assignment to outer array [duplicate]分配给外部数组时,python闭包/嵌套函数失败[重复]
【发布时间】:2018-09-19 17:40:04
【问题描述】:

似乎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();

【问题讨论】:

  • 在触摸函数中定义 p 似乎可以正常工作
  • 或者你可以定义def touch(e, p):并使用touch(i, p)调用
  • @Alexander : 你在哪里对,我改了标题/例子

标签: python closures


【解决方案1】:

因为您在touch 中分配p,所以它成为touch 中的一个局部变量,并有效地“隐藏”了所有其他名称p 在封闭范围内。为了告诉 Python 你实际上想引用outer 中的p,你应该使用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()

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

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

【讨论】:

  • 相当讨厌的陷阱。感谢您的提示。
猜你喜欢
  • 2012-08-18
  • 2020-10-17
  • 1970-01-01
  • 2016-06-05
  • 2017-04-26
  • 2018-05-26
  • 2019-09-19
  • 2020-12-26
  • 1970-01-01
相关资源
最近更新 更多