Python子类计数器
我有这个python代码。结果是TopTest: attr1=0, attr2=1
为X这很好,但结果是SubTest: attr1=2, attr2=3
Y,我不太明白。Python子类计数器
基本上,我有一个class属性,它是一个计数器,它运行在__init__ method
。当我启动Y时,计数器被设置为2,并且仅在属性被分配之后。我不明白为什么它从2开始。不应该子类复制超类和计数器重新启动在0?
class AttrDisplay:
def gatherAttrs(self):
attrs = []
for key in sorted(self.__dict__):
attrs.append('%s=%s' % (key, getattr(self, key)))
return ', '.join(attrs)
def __repr__(self):
return '[%s: %s]' % (self.__class__.__name__, self.gatherAttrs())
class TopTest(AttrDisplay):
count = 0
def __init__(self):
self.attr1 = TopTest.count
self.attr2 = TopTest.count+1
TopTest.count += 2
class SubTest(TopTest):
pass
X, Y = TopTest(), SubTest()
print(X)
print(Y)
当SubTest
一个新的实例被创建TopTest.__init__()
被称为 - 因为SubTest
继承TopTest.__init__()
- 这增加两个TopTest.count
。
而且,由于SubTest
从来没有定义一个类级别的count
变量,当执行SubTest.count
,Python的回落,并使用TopTest.count
。
此行为可以通过重新定义count
本地到SubTest
来解决。
class SubTest(TopTest):
count = 0
您近了 - 当您查找对象的属性时,您不一定查找属于该对象本身的属性。相反,查找遵循Python的method resolution order,这不是完全简单。在这种情况下,然而,仅执行三个步骤:
- 检查
Y
有一个名为count
属性。 - 它不,所以检查它的类
SubTest
是否有一个名为count
的属性。 - 它没有,所以检查其父母
TopTest
是否有一个名为count
的属性。它确实如此,因此访问它。
简而言之,当你访问Y.count
,你实际*问TopTest.count
。
还有,你必须在你的代码中的错误的事实 - 而不是它自己的子测试增量TopTest
的计数和。你的问题的标题说“子类计数器”,但由于你在__init__()
,我假设你正在寻找一个实例计数器(以计数子类我相当确定you'd need to use metaclasses)。对于self.__class__
这是一个完美的用例,它是一个包含对象类的属性!当你调用SubTest()
def __init__(self):
self.attr1 = self.__class__.count
self.attr2 = self.__class__.count + 1
self.__class__.count += 2
使用,SubTest.count
将会增加,而不是TopTest.count
:为了使用它。
您可以访问并明确使用TopTest.count
,并且您的子类将坚持这种明确性。您可能需要考虑使用type(self).count
,那么每个实例将使用自己的类的变量,该变量可以在每个子类中变成不同的变量。
为了让你的子类有它自己的类变量,只需添加一个count = 0
它的定义:
class SubTest(TopTest):
count = 0
如果你希望每个班有它自己的类变量含蓄,您可以使用元类来加入这个变量。
class MetaCount(type):
def __new__(cls, name, bases, attrs):
new_cls = super(MetaCount, cls).__new__(cls, name, bases, attrs)
new_cls.count = 0
return new_cls
class Parent(metaclass=MetaCount):
def __init__(self):
self.attr1 = self.count
self.attr2 = self.count + 1
type(self).count += 2 # self.count += 2 creates an *instance* variable
class Child(Parent):
pass
p, c = Parent(), Child()
print(p.count) # 2
print(c.count) # 2
此代码在Python 3中失败。[这是因为Python 3改变了指定元类的方式](https://*.com/questions/39013249/metaclass-in-python3-5)。您需要在Parent中执行'Parent(metaclass = MetaCount):'而不是'__metaclass__ = MetaCount'。 –
@Christian更新,谢谢,我的机器运行2.7,我懒得REPL –
它看起来像你想保留为TopTest
每个子类的每个实例的计数器,但你不想被宣布为每个子类一个新的count
类变量重复自己。你可以做到这一点使用一元类:
class TestMeta(type):
def __new__(cls, name, bases, attrs):
new_class = super().__new__(cls, name, bases, attrs)
new_class.count = 0
return new_class
class TopTest(AttrDisplay, metaclass=TestMeta):
def __init__(self):
self.attr1 = self.count
self.attr2 = self.count + 1
self.increment_count(2)
@classmethod
def increment_count(cls, val):
cls.count += val
class SubTest(TopTest):
pass
您x
和y
对象的count
属性现在应该是独立的,和TopTest
和SubTest
后续实例将递增count
:
>>> x, y = TopTest(), SubTest()
>>> x.attr2
1
>>> y.attr2
1
>>> y2 = SubTest()
>>> y2.attr2
3
然而,元类可能会引起混淆,应该只在真正需要时才使用。在您的特定情况下,它就会简单得多刚刚重新定义了count
类属性为TopTest
每个子类:
class SubTest(TopTest):
count = 0
'你可以通过重新定义里面SubTest'计数解决这个问题 - 但是要修改'TopTest.count '在init中,'SubTest.count'将永远是0 – then0rTh
@ then0rTh正确。我不确定我是否遵守。这不是OP想要的行为吗? –
我以为'SubTest.count'应该计算'SubTest'的实例,但我可能是错的 – then0rTh