python 中 class 变量的继承
Inheritance of class variables in python
试图理解 python 中的 oop 我遇到了这种令我困惑的情况,我找不到满意的解释...
我正在构建一个 Countable class,它有一个计数器属性,用于计算有多少 class 实例已被初始化。我希望在初始化给定 class 的 subclass(或 subsubclass)时也增加此计数器。这是我的实现:
class Countable(object):
counter = 0
def __new__(cls, *args, **kwargs):
cls.increment_counter()
count(cls)
return object.__new__(cls, *args, **kwargs)
@classmethod
def increment_counter(cls):
cls.counter += 1
if cls.__base__ is not object:
cls.__base__.increment_counter()
其中count(cls)
用于调试目的,稍后我将其记录下来。
现在,让我们来看看这个的一些子class:
class A(Countable):
def __init__(self, a='a'):
self.a = a
class B(Countable):
def __init__(self, b='b'):
self.b = b
class B2(B):
def __init__(self, b2='b2'):
self.b2 = b2
def count(cls):
print('@{:<5} Countables: {} As: {} Bs: {} B2s: {}'
''.format(cls.__name__, Countable.counter, A.counter, B.counter, B2.counter))
当我 运行 代码如下:
a = A()
a = A()
a = A()
b = B()
b = B()
a = A()
b2 = B2()
b2 = B2()
我得到以下输出,这对我来说很奇怪:
@A Countables: 1 As: 1 Bs: 1 B2s: 1
@A Countables: 2 As: 2 Bs: 2 B2s: 2
@A Countables: 3 As: 3 Bs: 3 B2s: 3
@B Countables: 4 As: 3 Bs: 4 B2s: 4
@B Countables: 5 As: 3 Bs: 5 B2s: 5
@A Countables: 6 As: 4 Bs: 5 B2s: 5
@B2 Countables: 7 As: 4 Bs: 6 B2s: 6
@B2 Countables: 8 As: 4 Bs: 7 B2s: 7
为什么一开始 A 和 B 的计数器都在递增,尽管我只调用 A()
?为什么在我第一次调用 B()
之后它的行为与预期的一样?
我已经发现,要获得我想要的行为,在每个子 class 处添加 counter = 0
就足够了,但我无法找到其行为为何如此的解释那....谢谢!
我添加了一些调试打印,为简单起见,将 class 创建限制为两个。这很奇怪:
>>> a = A()
<class '__main__.A'> incrementing
increment parent of <class '__main__.A'> as well
<class '__main__.Countable'> incrementing
@A Counters: 1 As: 1 Bs: 1 B2s: 1
>>> B.counter
1
>>> B.counter is A.counter
True
>>> b = B()
<class '__main__.B'> incrementing
increment parent of <class '__main__.B'> as well
<class '__main__.Countable'> incrementing
@B Counters: 2 As: 1 Bs: 2 B2s: 2
>>> B.counter is A.counter
False
为什么 B() 还没有初始化时,它指向与 A.counter 相同的变量,但是在创建单个对象后它是不同的?
您的代码的问题是 Countable
的子 class 没有它们自己的 counter
属性。他们只是从 Countable
继承了它,所以当 Countable
的 counter
发生变化时,它看起来像 child class 的 counter
也有变化。
最小示例:
class Countable:
counter = 0
class A(Countable):
pass # A does not have its own counter, it shares Countable's counter
print(Countable.counter) # 0
print(A.counter) # 0
Countable.counter += 1
print(Countable.counter) # 1
print(A.counter) # 1
如果 A
有自己的 counter
属性,一切都会按预期工作:
class Countable:
counter = 0
class A(Countable):
counter = 0 # A has its own counter now
print(Countable.counter) # 0
print(A.counter) # 0
Countable.counter += 1
print(Countable.counter) # 1
print(A.counter) # 0
但是如果所有这些 classes 共享相同的 counter
,为什么我们在输出中看到不同的数字?那是因为你实际上将 counter
属性添加到 child class 之后,代码如下:
cls.counter += 1
这相当于cls.counter = cls.counter + 1
。但是,了解 cls.counter
指的是什么很重要。在 cls.counter + 1
中,cls
还没有自己的 counter
属性,所以这实际上给了你 parent class 的 counter
。然后该值递增,cls.counter = ...
将 counter
属性添加到 child class 直到现在才存在。它本质上等同于写 cls.counter = cls.__base__.counter + 1
。您可以在此处查看实际效果:
class Countable:
counter = 0
class A(Countable):
pass
# Does A have its own counter attribute?
print('counter' in A.__dict__) # False
A.counter += 1
# Does A have its own counter attribute now?
print('counter' in A.__dict__) # True
那么这个问题的解决方案是什么?你需要一个metaclass。这使您可以在创建每个 Countable
subclass 时赋予其自己的 counter
属性:
class CountableMeta(type):
def __init__(cls, name, bases, attrs):
cls.counter = 0 # each class gets its own counter
class Countable:
__metaclass__ = CountableMeta
# in python 3 Countable would be defined like this:
#
# class Countable(metaclass=CountableMeta):
# pass
class A(Countable):
pass
print(Countable.counter) # 0
print(A.counter) # 0
Countable.counter += 1
print(Countable.counter) # 1
print(A.counter) # 0
试图理解 python 中的 oop 我遇到了这种令我困惑的情况,我找不到满意的解释... 我正在构建一个 Countable class,它有一个计数器属性,用于计算有多少 class 实例已被初始化。我希望在初始化给定 class 的 subclass(或 subsubclass)时也增加此计数器。这是我的实现:
class Countable(object):
counter = 0
def __new__(cls, *args, **kwargs):
cls.increment_counter()
count(cls)
return object.__new__(cls, *args, **kwargs)
@classmethod
def increment_counter(cls):
cls.counter += 1
if cls.__base__ is not object:
cls.__base__.increment_counter()
其中count(cls)
用于调试目的,稍后我将其记录下来。
现在,让我们来看看这个的一些子class:
class A(Countable):
def __init__(self, a='a'):
self.a = a
class B(Countable):
def __init__(self, b='b'):
self.b = b
class B2(B):
def __init__(self, b2='b2'):
self.b2 = b2
def count(cls):
print('@{:<5} Countables: {} As: {} Bs: {} B2s: {}'
''.format(cls.__name__, Countable.counter, A.counter, B.counter, B2.counter))
当我 运行 代码如下:
a = A()
a = A()
a = A()
b = B()
b = B()
a = A()
b2 = B2()
b2 = B2()
我得到以下输出,这对我来说很奇怪:
@A Countables: 1 As: 1 Bs: 1 B2s: 1
@A Countables: 2 As: 2 Bs: 2 B2s: 2
@A Countables: 3 As: 3 Bs: 3 B2s: 3
@B Countables: 4 As: 3 Bs: 4 B2s: 4
@B Countables: 5 As: 3 Bs: 5 B2s: 5
@A Countables: 6 As: 4 Bs: 5 B2s: 5
@B2 Countables: 7 As: 4 Bs: 6 B2s: 6
@B2 Countables: 8 As: 4 Bs: 7 B2s: 7
为什么一开始 A 和 B 的计数器都在递增,尽管我只调用 A()
?为什么在我第一次调用 B()
之后它的行为与预期的一样?
我已经发现,要获得我想要的行为,在每个子 class 处添加 counter = 0
就足够了,但我无法找到其行为为何如此的解释那....谢谢!
我添加了一些调试打印,为简单起见,将 class 创建限制为两个。这很奇怪:
>>> a = A()
<class '__main__.A'> incrementing
increment parent of <class '__main__.A'> as well
<class '__main__.Countable'> incrementing
@A Counters: 1 As: 1 Bs: 1 B2s: 1
>>> B.counter
1
>>> B.counter is A.counter
True
>>> b = B()
<class '__main__.B'> incrementing
increment parent of <class '__main__.B'> as well
<class '__main__.Countable'> incrementing
@B Counters: 2 As: 1 Bs: 2 B2s: 2
>>> B.counter is A.counter
False
为什么 B() 还没有初始化时,它指向与 A.counter 相同的变量,但是在创建单个对象后它是不同的?
您的代码的问题是 Countable
的子 class 没有它们自己的 counter
属性。他们只是从 Countable
继承了它,所以当 Countable
的 counter
发生变化时,它看起来像 child class 的 counter
也有变化。
最小示例:
class Countable:
counter = 0
class A(Countable):
pass # A does not have its own counter, it shares Countable's counter
print(Countable.counter) # 0
print(A.counter) # 0
Countable.counter += 1
print(Countable.counter) # 1
print(A.counter) # 1
如果 A
有自己的 counter
属性,一切都会按预期工作:
class Countable:
counter = 0
class A(Countable):
counter = 0 # A has its own counter now
print(Countable.counter) # 0
print(A.counter) # 0
Countable.counter += 1
print(Countable.counter) # 1
print(A.counter) # 0
但是如果所有这些 classes 共享相同的 counter
,为什么我们在输出中看到不同的数字?那是因为你实际上将 counter
属性添加到 child class 之后,代码如下:
cls.counter += 1
这相当于cls.counter = cls.counter + 1
。但是,了解 cls.counter
指的是什么很重要。在 cls.counter + 1
中,cls
还没有自己的 counter
属性,所以这实际上给了你 parent class 的 counter
。然后该值递增,cls.counter = ...
将 counter
属性添加到 child class 直到现在才存在。它本质上等同于写 cls.counter = cls.__base__.counter + 1
。您可以在此处查看实际效果:
class Countable:
counter = 0
class A(Countable):
pass
# Does A have its own counter attribute?
print('counter' in A.__dict__) # False
A.counter += 1
# Does A have its own counter attribute now?
print('counter' in A.__dict__) # True
那么这个问题的解决方案是什么?你需要一个metaclass。这使您可以在创建每个 Countable
subclass 时赋予其自己的 counter
属性:
class CountableMeta(type):
def __init__(cls, name, bases, attrs):
cls.counter = 0 # each class gets its own counter
class Countable:
__metaclass__ = CountableMeta
# in python 3 Countable would be defined like this:
#
# class Countable(metaclass=CountableMeta):
# pass
class A(Countable):
pass
print(Countable.counter) # 0
print(A.counter) # 0
Countable.counter += 1
print(Countable.counter) # 1
print(A.counter) # 0