为什么对象 属性 中的存储函数被覆盖 (Python)?

Why is a stored function in an object property being overwritten (Python)?

以下是我的问题的最小工作示例,其中我有一个包含鱼对象数组的学校对象,我希望每条鱼都具有基于其索引的属性。我所做的只是设置属性,然后在初始化期间和稍后 class 完全实例化后将它们打印出来。问题是设置这些属性似乎对函数不起作用。此外,根据是否使用枚举(我想了解),它的表现也有所不同。

在下面的代码中,输出显示我学校对象中鱼的 'func' 属性 被覆盖,但 'x' 属性 没有.

class Fish:
    def __init__(self):
        self.x = -1
        self.func = lambda y: -1


class School:
    def __init__(self):
        self.fishList = [Fish() for i in range(2)]
        for fishIndex, f in enumerate(self.fishList):
            f.x = fishIndex
            f.func = lambda y: fishIndex
        print("No enumerate:")
        for f in self.fishList:
            print(f.x, f.func(0))
        print("Enumerate")
        for fishIndex, f in enumerate(self.fishList):
            print(f.x, f.func(0))

mySchool = School()
print("-----------------------------------------")
print("No enumerate:")
for f in mySchool.fishList:
    print(f.x, f.func(0))
print("Enumerate:")
for fishIndex, f in enumerate(mySchool.fishList):
    print(f.x, f.func(0))


输出:

No enumerate:
0 1
1 1
Enumerate
0 0
1 1
-----------------------------------------
No enumerate:
0 1
1 1
Enumerate:
0 1
1 1

到目前为止,我已经追踪到问题源于我如何储存鱼。如果我将鱼单独存储,而不是存储在数组中,则函数会正确保存。所以这让我认为这是一个 mutability/pointers 问题。但如果是这种情况,我预计 'x' 也不会正确坚持。

编辑:澄清一下,在输出中,虚线之前的第一个“枚举”是我对所有输出的预期。

func的定义中,fishIndex是一个自由变量。这意味着名称本身,而不是 fishIndex 的当前值,是函数定义的一部分。

当函数被调用时,它会使用__init__.

中定义的局部变量fishIndex的当前值

__init__ 中,当您将它用作 for 循环中的索引变量时,该变量会发生变化。

__init__之外,fishIndex仍然引用__init__中定义的变量,即使那个函数returns。我们说 func 是一个 闭包 在其定义范围内的变量上。

您在全局范围内的循环中重复使用名称 fishIndex 的事实不会影响 func.

的行为