此单例模式实现按预期工作,但出现意外情况(请参阅测试)。怎么会出错?
代码:
class Singleton(object):
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(Singleton, cls).__new__(
cls, *args, **kwargs)
print("new class created")
return cls._instance
class CounterClassBase(object):
def __init__(self, *args, **kwargs):
super(CounterClassBase, self).__init__(*args, **kwargs)
print("Counter class init in base")
class Counter(CounterClassBase, Singleton):
counter = None
def __init__(self, *args, **kwargs):
super(Counter, self).__init__(*args, **kwargs)
print("Counter class init")
self.counter = 0
import pdb
pdb.set_trace()
测试:
(Pdb) var1 = Counter()
new class created
Counter class init in base
Counter class init
(Pdb) var2 = Counter()
Counter class init in base
Counter class init
(Pdb) var3 = Counter()
Counter class init in base
Counter class init
(Pdb) var1.counter = 3
(Pdb) var1.counter
3
(Pdb) var2.counter
3
(Pdb) var3.counter
3
(Pdb) var4 = Counter()
Counter class init in base
Counter class init
(Pdb) var4.counter
0
(Pdb)
var4.counter
的预期值为3,而不是0.
如答案所述,我的测试错了。很明显,打印了“Counter class init”语句,因此调用 init (),因此每次创建新的类实例时都会初始化变量。
所以我改变了我的代码如下(来源:https://stackoverflow.com/a/8665179/1952991):
class Singleton(type):
def __init__(self, *args, **kwargs):
super(Singleton, self).__init__(*args, **kwargs)
self.__instance = None
def __call__(self, *args, **kwargs):
if self.__instance is None:
self.__instance = super(Singleton, self).__call__(*args, **kwargs)
return self.__instance
class Counter(object):
__metaclass__ = Singleton
counter = None
def __init__(self, *args, **kwargs):
print("Counter class init")
self.counter = 0
答案 0 :(得分:2)
所以你的测试是错误的。 Var 4是正确的。即使认为它使用的是最初创建的类,它也会在self.counter == 0
中设置Counter().__init__()
。
我希望如果再次运行测试并同时实例化所有计数器,然后将变量设置为3,它们都将为3.我不确定这是否是您想要的行为。
答案 1 :(得分:2)
它为零,因为Counter.__init__
将其指定为零。根据需要,只有一个单例对象,但Counter.__init__
和CounterBase.__init__
中的代码在每次调用时都会执行。要使其工作,您必须初始化Singleton
类本身的计数器属性。我可以看到你想把计数和单身方面分开,但为此他们有点太分开。
答案 2 :(得分:2)
原因是每次调用构造函数时都会调用__init__
。您已重写__new__
以始终返回相同的实例,但每次在__new__
之后仍然会调用将计数器设置为0的初始化代码。如果您在分配var1.counter
后检查var4
,则会发现它也会返回0.
不要这样做,请考虑以下事项:
class Counter():
counter = 0 # Initialize the field here, rather than in __init__
def __init__(self, *args, **kwargs):
super(Counter, self).__init__(*args, **kwargs)
print("Counter class init")
这将仅初始化counter
字段一次,并且在使用Counter()
构造函数分配新变量时不会覆盖它。