在封闭的函数范围中绑定变量

时间:2015-04-09 13:17:15

标签: python python-3.x

我正在阅读Wes McKinney的 Python for Data Analysis 。我在这部分遇到了麻烦:

这是一个函数,它返回一个跟踪调用它的参数的函数:

 def make_watcher():
     have_seen = {}
     def has_been_seen(x):
         if x in have_seen:
             return True
         else:
             have_seen[x] = True
             return False
     return has_been_seen

在一系列整数上使用它我得到:

 In [496]: watcher = make_watcher()
 In [497]: vals = [5, 6, 1, 5, 1, 6, 3, 5]
 In [498]: [watcher(x) for x in vals]
 Out[498]: [False, False, False, True, True, True, False, True]

然而,要记住的一个技术限制是,当你  可以改变任何内部状态对象(比如添加键值对)  一个字典),你不能在封闭的函数范围内绑定变量。  解决此问题的一种方法是修改字典或列表而不是  绑定变量:

 def make_counter():
     count = [0]
     def counter():
         # increment and return the current count
         count[0] += 1
         return count[0]
     return counter

 counter = make_counter()

在上面的文本中,作者的意思是“你不能在封闭的函数范围中绑定变量”?请详细说明这一说法。

2 个答案:

答案 0 :(得分:2)

该示例旨在演示哪些方法有效,但未显示哪些方法无效。这是一个例子:

def make_counter():
    count = 0
    def counter():
        # DOESN'T WORK
        count += 1
        return count
    return counter

这不起作用,因为你试图将counter重新绑定到一个新对象,整数1,你不能这样做。

答案 1 :(得分:1)

它不起作用,因为int是Python中的不可变对象 - 一旦创建它们就无法更改 - 而list是可变的。在Python中,最好将变量视为名称标签而不是容纳值的容器。该名称只是对实际存储值的物理内存块的一个很好的引用。因此,当您执行count += 1时,您实际上是将名称标记count移动到物理内存中的新地址,该地址的值比前一个值count多一个。使用id()可以看到这一点。

In [1]: a = 0

In [2]: id(a)
Out[2]: 140696027710384

In [3]: a += 1

In [4]: id(a)
Out[4]: 140696027710360

正如您所知,当我们增加a时,其ID会发生变化。现在让我们对列表做同样的事情:

In [1]: a = [0]

In [2]: id(a)
Out[2]: 4389820176

In [3]: a[0] += 1

In [4]: id(a)
Out[4]: 4389820176

In [5]: a.append(1)

In [6]: id(a)
Out[6]: 4389820176

正如您所看到的,当我们在列表中增加一个值时,列表的id不会改变,当我们向列表中添加一个元素时,id也不会改变。