Reputation: 1091
i've this implementation of singleton pattern in python, but i've noticed that the init is called, uselessly, everytime i call MyClass, despite the same instance is returned.
How can i avoid it?
class Test(object):
def __init__(self, *args, **kwargs):
object.__init__(self, *args, **kwargs)
class Singleton(object):
_instance = None
def __new__(cls):
if not isinstance(cls._instance, cls):
cls._instance = object.__new__(cls)
return cls._instance
class MyClass(Singleton):
def __init__(self):
print("should be printed only 1 time")
self.x=Test()
pass
a = MyClass() # prints: "should be printed only 1 time"
b = MyClass() # prints ( again ): "should be printed only 1 time"
print(a,b) # prints: 0x7ffca6ccbcf8 0x7ffca6ccbcf8
print(a.x,b.x) # prints: 0x7ffca6ccba90 0x7ffca6ccba90
Upvotes: 7
Views: 8008
Reputation: 11
It's never too late i guess.. The answer is in fact in the question.
i've noticed that the init is called, uselessly, everytime i call MyClass
So, just remove access to it (and probably from memory)...
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
print('__new__()')
if cls._instance is None:
cls._instance = super().__new__(cls) # Everything is normal python, no stress !
elif cls.__init__.__name__ == '__init__':
print("No need __init__ anymore, sorry, goodbye !")
cls.__init__ = lambda *args: None # The __name__ of __init__ function is "<lambda>" now.
print(cls._instance)
return cls._instance # After this return, python call internally __init__(). Even if you set cls._instance to None, the __init__ function is not present anymore.
class App(Singleton):
def __init__(self, number):
print(f"Init only one time with number {number} !")
if __name__ == "__main__":
App(24)
App(53)
App(63)
App(63)
Output:
__new__()
<App object at 3ffe5e90>
Init only one time with number 24 !
__new__()
No need __init__ anymore, sorry, goodbye !
<App object at 3ffe5e90>
__new__()
<App object at 3ffe5e90>
__new__()
<App object at 3ffe5e90>
Upvotes: 0
Reputation: 437
Another way to create a singleton object would be to have another class method say get_instance which would ensure there is a singleton object for your class .. here is the snippet :
class Singleton(object):
__instance = None
@classmethod
def get_instance(cls):
if not cls.__instance:
Singleton()
return cls.__instance
def __init__(self):
if Singleton.__instance is None:
Singleton.__instance = self
print("Object initialized")
singleton_1 = Singleton.get_instance()
singleton_2 = Singleton.get_instance()
if singleton_1 is singleton_2:
print("Same")
else:
print("Different")
The above snippet produces following output :
Object initialized
Same
Upvotes: 0
Reputation: 123531
Another simple, but totally viable way to implement what you want, which doesn't require super or meta classes, is to just make your class a Python module, which are essentially Singleton objects.
Here's what I mean:
myclass.py
:
class Test(object):
pass
class MyClass(object):
def __init__(self):
print("in MyClass.__init__, should be printed only 1 time")
self.x = Test()
def __call__(self, *args, **kwargs):
classname = type(self).__name__
return globals()[classname]
MyClass = MyClass()
client.py
:
from myclass import MyClass
a = MyClass()
b = MyClass()
print(a, b)
print(a.x, b.x)
Output:
in MyClass.__init__, should be printed only 1 time
<myclass.MyClass object at 0x02200B30> <myclass.MyClass object at 0x02200B30>
<myclass.Test object at 0x02200C10> <myclass.Test object at 0x02200C10>
It's possible to derive a subclass from MyClass, but you'd have to do it something like this:
class Derived(type(MyClass)):
def __init__(self):
print("in Derived.__init__, should be printed only 1 time")
Derived = Derived()
Afterwards you could add this to 'client.py':
from myclass import Derived
a = Derived()
b = Derived()
print(a,b)
print(a.x,b.x) # AttributeError: 'Derived' object has no attribute 'x'
Upvotes: 5
Reputation: 5054
The problem is that __new__
doesn't return an object, it returns an unitialized object on which __init__
is called afterwards.
You can't avoid that at all. What you can do is the following(using metatypes):
class Singleton(type):
def __init__(self, name, bases, mmbs):
super(Singleton, self).__init__(name, bases, mmbs)
self._instance = super(Singleton, self).__call__()
def __call__(self, *args, **kw):
return self._instance
class Test(metaclass = Singleton):
# __metaclass__ = Singleton # in python 2.7
def __init__(self, *args, **kw):
print("Only ever called once")
Upvotes: 13
Reputation: 122151
__init__
, if implemented, is always called on whatever gets returned by __new__
; in general, you should be implementing one or the other, not both. You could implement __new__
on MyClass
, instead, and initialise the x
attribute only if it doesn't already exist:
class MyClass(Singleton):
def __new__(cls):
inst = super(MyClass, cls).__new__(cls)
if not hasattr(inst, 'x'):
print("should be printed only 1 time")
inst.x = Test()
return inst
In use:
>>> a = MyClass()
should be printed only 1 time
>>> b = MyClass()
>>> a is b
True
>>> a.x is b.x
True
Upvotes: 0