I'm trying to implement a singleton in Python, and after reading this post I find myself even more confused than before. There are way too many answers, and many of them have received their fair number of votes. Now the problem may not be that I have a singleton, but the fact that the state has to be initialized only once. I tried a couple of implementations SingletonA
and SingletonB
but I can't manage this to work. For my real problem, the __init__
function is quite heavy so I have to do it only once. This is what I have so far:
class ClassA:
def __init__(self):
# some state
print("Creating state in A")
self.X = 1.
self.Y = 2.
class SingletonA(ClassA):
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(SingletonA, cls).__new__(
cls, *args, **kwargs)
return cls._instance
class ClassB:
__shared_state = {}
def __init__(self):
if not bool(ClassB.__shared_state):
# some state
print("Creating state in B")
self.X = 1.
self.Y = 2.
self.__dict__ = self.__shared_state
def singleton(cls):
obj = cls()
# Always return the same object
cls.__new__ = staticmethod(lambda cls: obj)
# Disable __init__
try:
del cls.__init__
except AttributeError:
pass
return cls
@singleton
class SingletonB(ClassB):
pass
if __name__ == "__main__":
a1 = SingletonA()
a2 = SingletonA()
if (id(a1) == id(a2)):
print("Same",a1.X, a1.Y)
else:
print("Different",a1.X, a1.Y)
b1 = SingletonB()
b2 = SingletonB()
if (id(b1) == id(b2)):
print("Same",b1.X, b1.Y)
else:
print("Different",b1.X, b1.Y)
Now this is printing:
$ python singleton.py
Creating state in B
Creating state in B
Same 1.0 2.0
Creating state in A
Creating state in A
Same 1.0 2.0
Pointing to the fact that indeed I have a singleton class, but I want to avoid the creation of state.
This cannot work, because __init__
method is called after object creation through __new__
. Extract from Python Language Reference
If
__new__()
returns an instance of cls, then the new instance's__init__()
method will be invoked like__init__(self[, ...])
, whereself
is the new instance and the remaining arguments are the same as were passed to__new__()
.
You should instead have a special initialization method distinct from __init__
and called at _instance
creation.
Code could be (no need for a parent class, so I omitted it):
class SingletonA:
_instance = None
def __init__(self):
# some state
print("Creating dummy state in SingletonA")
def _init(self):
# some state
print("Creating state in SingletonA")
self.X = 1.
self.Y = 2.
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super(SingletonA, cls).__new__(
cls, *args, **kwargs)
cls._instance._init()
return cls._instance
But in fact, you can simply build the instance at declaration time:
class SingletonA:
def __init__(self):
# some state
print("Creating dummy state in SingletonA")
def _init(self):
# some state
print("Creating state in A")
self.X = 1.
self.Y = 2.
def __new__(cls, *args, **kwargs):
return cls._instance
SingletonA._instance = super(SingletonA, SingletonA).__new__(SingletonA)
SingletonA._instance._init()
Both ways would cause following output (for SingletonA part):
Creating state in A
Creating dummy state in SingletonA
Creating dummy state in SingletonA
('Same', 1.0, 2.0)
Completely removing the __init__
method would cause one single initialization.
The annotation version could be:
class ClassB:
def __init__(self):
# some state
print("Creating state in B")
self.X = 1.
self.Y = 2.
def singleton(cls):
obj = cls()
# Always return the same object
cls._instance = obj
cls.__new__ = staticmethod(lambda cls: cls._instance)
# Disable __init__
cls.__init__ = (lambda self: None)
return cls
@singleton
class SingletonB(ClassB):
pass
simply storing the singleton instance in the class itself
If I understand you correctly, this is all what you need:
# edited according to discussion in comments
class C:
_shared_dict = None
def __init__(self):
if self._shared_dict is None:
print("initializing")
self.x = 1
self.y = 2
self.__class__._shared_dict = self.__dict__
else:
self.__dict__ = self._shared_dict
a=C()
b=C()
print(id(a), a.x, a.y)
print(id(b), b.x, b.y)
All instances will share the same data and this data will be computed only once.
Please note that you can refer to the shared data as self._shared_dict
when reading, but must use the full name of a class attribute self.__class__._shared_dict
when writing to it.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.