更改 __init__ 中的 class 属性

Changing a class attribute within __init__

我正在查看 Stack Overflow 问题 Counting instances of a class?,但我不确定为什么该解决方案有效而使用简单加法的解决方案却无效。我想这更多的是关于如何存储和访问 class 与实例变量的问题。

这是我认为应该有效的代码,但对每个 id:

生成 4
class foo():
      num = 3    # trying 3 instead of 0 or 1 to make sure the add is working

      def __init__(self):
        self.num += 1
        self.id = self.num

f = foo()
g = foo()

print f.id    # 4
print g.id    # 4

self.num +=1 语句有点工作(添加正在发生,但不是赋值)。

到底发生了什么导致此作业在这里失败,而 itertools.count 作业在另一个问题的解决方案中成功?

self.num += 1 的意思是,基本上,'取 self.num 的值,增加它,然后分配给 self.num

self 上查找属性,如果没有相应的实例变量,将查找 class 变量。但是,assigning 将始终写入实例变量。因此,这试图找到一个实例 var,失败,回退到 class var,获取值,递增它,然后将 分配给实例 var.

链接问题的答案有效的原因是没有分配正在进行;他们直接在 class 变量上调用 next(),它的值发生了变化,但名称没有重新分配。

整数不实现 __iadd__(就地添加,对于 +=),因为它们是不可变的。解释器回退到标准分配和 __add__ 相反,所以行:

self.num += 1

变为:

self.num = self.num + 1

在右侧,如您所料,您通过 self.num 获得 foo.num(即 3),但这里有趣的是 分配到实例属性 num shadows class 属性。所以该行实际上等同于:

self.num = foo.num + 1  # instance attribute equals class attribute plus one

所有 实例 self.num == 4 结束,而 class 仍然是 foo.num == 3。相反,我怀疑你想要的是:

foo.num += 1  # explicitly update the class attribute

或者,您可以将其实现为 @classmethod,更明确地处理 class:

class Foo():  # note naming convention

    num = 3

    def __init__(self):
        self.increment()
        self.id = self.num  # now you're still accessing the class attribute

    @classmethod
    def increment(cls):
        cls.num += 1

扩充赋值不更新 class 变量。它这样做:

tmp = self.num
self.num = tmp.__iadd__(1)

请注意 self.num 那里的作业!因此,您的 class 属性保持不变。整数的 object.__iadd__() method 不能就地改变数字,因为整数是不可变的,所以 foo.num 永远不会改变。

您需要明确引用 class 变量:

foo.num += 1

self.__class__.num += 1