Python 静态变量解析

0 投票
4 回答
1981 浏览
提问于 2025-04-17 19:34

我正在尝试为某个特定的类创建一个计数器,用来统计这个类创建的所有实例数量。很明显,我的代码没有让 self.__class__.counter 增加到超过1。我到底哪里出错了呢?

 class Feature(object):
    counter = 0
    def __init__(self, line):
        self.id = self.__class__.counter
        self.__class__.counter = self.__class__.counter + 1
    def __repr__(self):
        return "Feature: id=%d name=%s" % (self.id,self.name,)

更新: 其实这段代码是按预期工作的。聪明的人们留下了一些非常详细的解释,讲述了在Python中如何处理静态变量。我投票关闭了我的问题,但我不希望这些答案消失。

4 个回答

1

为了更好地说明 self.__class__.attributeClassName.attribute 之间的区别:

class Feature(object):
    counter = 0

    def __init__(self):
        self.id = self.__class__.counter
        self.__class__.counter += 1

class Sub(Feature):
    def __init__(self):
        super(Sub, self).__init__()        

print [(vars(Feature()), vars(Sub())) for i in xrange(3)]
print Feature.counter, Sub.counter, id(Feature.counter) == id(Sub.counter)


class Feature(object):
    counter = 0

    def __init__(self):
        self.id = Feature.counter
        Feature.counter += 1

class Sub(Feature):
    def __init__(self):
        super(Sub, self).__init__()


print [(vars(Feature()), vars(Sub())) for i in xrange(3)]
print Feature.counter, Sub.counter, id(Feature.counter) == id(Sub.counter)

>>> [({'id': 0}, {'id': 1}), ({'id': 1}, {'id': 2}), ({'id': 2}, {'id': 3})]
3 4 False
>>> [({'id': 0}, {'id': 1}), ({'id': 2}, {'id': 3}), ({'id': 4}, {'id': 5})]
6 6 True
1

在计算实例的时候,我更喜欢这种方式:

from itertools import count

class MyClass():
  _getId = count(0).next
  def __init__(self):
    self.id = self._getId()
6

这是我会做的(虽然你的代码对我来说运行得很好,所以我怀疑你观察到的问题可能不是因为代码本身)。

class Feature(object):
    counter = 0

    def __init__(self):
        self.id = Feature.counter
        Feature.counter += 1

如果你愿意,可以把 Feature 替换成 type(self)(或者 self.__class__,效果是一样的),但要注意它们在有子类的情况下表现会有所不同(这可能就是你的问题所在)。

在Python中,使用类和实例变量的规则非常简单,记住这些规则会很有帮助:

  1. 当你用 self.name 读取一个属性的值时,如果 self 里没有直接包含名为 name 的属性,它会先查找 self 的类,然后再查找基类的层级。
  2. 当你用 self.name = ... 写入一个属性的值时,总是会直接在 self 中绑定 name

特别是,当你写入一个值时,之前读取同一个属性的地方并不重要

所以如果在你的实际程序中,你实例化了 Feature 的一个 subclass,我称它为 Sub,那么这一行:

self.__class__.counter = self.__class__.counter + 1

可能不会如你所预期的那样工作。它会在第一次创建 Sub 的实例时从 Feature.counter 读取值,但会写入到 Sub.counter。之后的读取会去 Sub.counter(因为它现在存在了),所以 Sub 的实例会得到递增的ID,但它们并没有增加 Feature.counter 的值,因此其他 Feature 的子类实例可能会得到重复的ID。

所以如果我把 counter 看作是一个在 Feature 命名空间中的全局变量,并且我希望所有 Feature 的子类共享同一个计数器,我会使用 Feature.counter。如果我希望每一个 Feature 的子类都有自己的独立计数器,我会使用 type(self).counter(但那样的话你需要做一些事情来初始化它们)。

撰写回答