Python Singleton基类意外行为

时间:2014-02-03 00:55:12

标签: python singleton

此单例模式实现按预期工作,但出现意外情况(请参阅测试)。怎么会出错?

代码:

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

3 个答案:

答案 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()构造函数分配新变量时不会覆盖它。