Python闭包中的数字变量范围(Python v2.5.2)

时间:2010-09-23 03:09:27

标签: python

我有一个嵌套函数,我试图访问父作用域中分配的变量。从next()函数的第一行开始,我可以看到pathnodes_done按预期分配。 distancecurrentprobability_left没有任何价值,导致NameError被抛出。

我在这里做错了什么?如何从current函数访问和修改distanceprobability_leftnext()的值?

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 个答案:

答案 0 :(得分:4)

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会将其解释为创建新的局部变量path阴影父项path)的嵌套函数中。

有时使用的一个选项是将您想要保留的所有内容保留在dict的嵌套范围中:

def A():
    state = {
        'path': [],
        'current': 0,
        # ...
    }

    def B():
        state['current'] = 3

答案 1 :(得分:2)

简短的回答是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

答案 2 :(得分:2)

如果您正在使用Python 3,则可以使用nonlocal语句(documentation)使这些变量存在于当前范围内,例如:

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