I'm learning about how to create singletons in Python (please, don't go into why singletons are bad or such, it's not the topic here).
Based on one implementation, I tried doing it like this:
class Singleton(object):
def __init__(self, klass):
print "S init"
self.klass = klass
self.instance = None
def __call__(self, *args, **kwargs):
print "S call"
if self.instance is None:
self.instance = self.klass(*args, **kwargs)
else:
self.instance(*args, **kwargs)
return self.instance
@Singleton
class KlasseA:
def __new__(cls, *args, **kwargs):
print "KA new"
def __init__(self, s):
print "KA init"
self.__init2(s)
def __call__(self, s=None):
print "KA call"
self.__init2(s)
def __init2(self, s):
if s: self.s = s
@Singleton
class KlasseB:
def __new__(cls, *args, **kwargs):
print "KB new"
def __init__(self, s):
print "KB init"
self.__init2(s)
def __call__(self, s=None):
print "KB call"
self.__init2(s)
def __init2(self, s):
if s: self.s = s
Now, testing the above by appending the following lines in the same .py file:
print ""
a = KlasseA('one')
print "a -> ", id(a), a.s
b = KlasseA('two')
print "b -> ", id(b), b.s
print "a -> ", id(a), a.s
c = KlasseA()
print "c -> ", id(c), c.s
print "b -> ", id(b), b.s
print "a -> ", id(a), a.s
d = KlasseB('three')
print "d -> ", id(d), d.s
print "a -> ", id(a), a.s
I got the following:
S init
S init
S call
KA init
a -> 140525844905496 one
S call
KA call
b -> 140525844905496 two
a -> 140525844905496 two
S call
KA call
c -> 140525844905496 two
b -> 140525844905496 two
a -> 140525844905496 two
S call
KB init
d -> 140525844905568 three
a -> 140525844905496 two
So, the singleton decorator indeed works. What I don't understand here:
I thought decorators redefined the call, so the call KlasseA()
is effectively a call to Singleton(KlasseA)()
. Shouldn't this result in a new Singleton
instance for every KlasseA()
invocation?
I noticed that there were two "S init" lines, most likely because Singleton()
was invoked during declaration of KlasseA
and KlasseB
. Thus, there were two instances of Singleton
was created. Where were these instances being kept?
Is there a possible 'gotcha' with the above Singleton decorator recipe?
Decorators are applied when the class is defined, not when it is instantiated. Regarding your point 1, it's really equivalent to:
class KlasseA():
...
KlasseA = Singleton(KlasseA)
a = KlasseA()
Regarding your point 2, the names KlasseA
and KlasseB
are bound to instances of Singleton
, as shown above.
Regarding point 3, I'm not sure it makes sense to invoke the __call__
method of the single instance of a class just because you tried to create a new instance. That is, I would think Singleton.__call__
should simply be
def __call__(self, *args, **kwargs):
if self.instance is None:
self.instance = self.klass(*arg, **kwargs)
return self.instance