代码之家  ›  专栏  ›  技术社区  ›  Eric Schoonover thSoft

python闭包中的数值变量范围(python v2.5.2)

  •  2
  • Eric Schoonover thSoft  · 技术社区  · 14 年前

    我有一个嵌套函数,尝试访问在父作用域中分配的变量。从第一行 next() 功能我能看到 path nodes_done 按预期分配。 distance , current probability_left 没有值,导致 NameError 被抛出。

    我在这里做错什么了?如何访问和修改 现在的 , 距离 概率剩余 来自 下() 功能?

    def cheapest_path(self):
        path = []
        current = 0
        distance = 0
        nodes_done = [False for _ in range(len(self.graph.nodes))]
        probability_left = sum(self.graph.probabilities)
    
        def next(dest):
            log('next: %s -> %s distance(%.2f), nodes_done(%s), probability_left(%.2f)' % (distance,self.graph.nodes[current],self.graph.nodes[dest],str(nodes_done),probability_left))
            path.append((current, distance, nodes_done, probability_left))
    
            probability_left -= self.graph.probabilities[current]
            nodes_done[current] = True
            distance = self.graph.shortest_path[current][dest]
            current = dest
    
        def back():
            current,nodes_done,probability_left = path.pop()
    
    3 回复  |  直到 14 年前
        1
  •  4
  •   Amber    14 年前

    python的嵌套作用域的工作方式,您永远不能 分配 到父范围中的变量,除非它是全局变量(通过 global 关键字)。这在python 3中发生了变化(添加了 nonlocal 但是有了2.x你就卡住了。

    相反,您必须使用通过引用存储的数据类型来解决这一问题:

    def A():
        foo = [1]
        def B():
            foo[0] = 2 # since foo is a list, modifying it here modifies the referenced list
    

    请注意,这就是列表变量工作的原因——它们是通过引用存储的,因此修改列表会修改原始引用列表。如果你想做点什么 path = [] 在嵌套函数内部,它将不起作用,因为它将实际分配给 path (Python将解释为创建一个新的局部变量 路径 在嵌套函数中, 阴影 父母的 路径 )

    有时使用的一个选项是,只保留要在 dict :

    def A():
        state = {
            'path': [],
            'current': 0,
            # ...
        }
    
        def B():
            state['current'] = 3
    
        2
  •  2
  •   Mike Axiak    14 年前

    简而言之,python没有适当的词汇范围支持。如果这样做了,就必须有更多的语法来支持这种行为(即,声明变量作用域的var/def/my关键字)。

    除了实际的词汇范围之外,最好的方法是将数据存储在环境数据结构中。一个简单的例子是列表,例如:

    def cheapest_path(self):
        path = []
        path_info = [0, 0]
        nodes_done = [False for _ in range(len(self.graph.nodes))]
        probability_left = sum(self.graph.probabilities)
    
        def next(dest):
            distance, current = path_info
            log('next: %s -> %s distance(%.2f), nodes_done(%s), probability_left(%.2f)' %     (distance,self.graph.nodes[current],self.graph.nodes[dest],str(nodes_done),probability_left))
            path.append((current, distance, nodes_done, probability_left))
    
            probability_left -= self.graph.probabilities[current]
            nodes_done[current] = True
            path_info[0] = self.graph.shortest_path[current][dest]
            path_info[1] = dest
    
        def back():
            current,nodes_done,probability_left = path.pop()
    

    你可以这样做,也可以检查魔法。关于这方面的更多历史,请阅读 thread .

        3
  •  2
  •   Ray    14 年前

    如果您碰巧使用的是Python3,那么可以使用 nonlocal 声明( documentation )使这些变量存在于当前范围内,例如:

    def next(dest):
        nonlocal distance, current, probability_left
        ...