Singleton is a design pattern that allows a class to have only one instance object. By using this, for example, by making one class for connecting to the database, you can write code on the assumption that multiple instances do not operate the database at the same time, so you can manage synchronization and data in memory. You will be able to. It also makes it easier to control concurrency. For example, if you have a document that reserves a unique document ID, it's better to have one utility to do that, so it's a good idea to use the Singleton pattern.
__new__ ()
methodYou can overwrite the __new__ ()
method and implement it as follows:
class Singleton(object):
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls, *args, **kwargs)
return cls._instance
Create multiple instances from this class and check the ID. You can see that the same object has been created.
obj1 = Singleton()
obj2 = Singleton()
print(id(obj1) == id(obj2))
# True
print(obj1 == obj2)
# True
However, this method has some pitfalls. Create a subclass that inherits this class and create an instance for each.
class SubSingleton(Singleton):
pass
print(Singleton())
# <__main__.Singleton object at 0x10cfda9e8>
print(SubSingleton())
# <__main__.Singleton object at 0x10cfda9e8>
Looking at this result, the subclass instance is created as an instance of the base class. Not only that, there is also the problem that the behavior changes depending on the order of instance creation.
class SubSingleton(Singleton):
pass
print(SubSingleton())
# <__main__.SubSingleton object at 0x10c3f57b8>
print(Singleton())
# <__main__.Singleton object at 0x10c3f5748>
If the behavior differs depending on the order, it will be difficult to predict the behavior. Debugging becomes difficult, especially for large applications. This is because the application may be destroyed depending on the user's behavior and the order of import.
In general, you should avoid inheriting and using the Singleton class. The method of overwriting the __new__ ()
method is also a safe implementation unless you create a subclass. Depending on the language, for example in C # you can prohibit inheritance by using the sealed modifier. However, Python cannot prohibit inheritance and can be implemented incorrectly. Therefore, it is safer to consider the case where it is inherited.
You can make a class a Singleton by overwriting the __call__ ()
method of the metaclass.
class MetaSingleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Singleton(metaclass = MetaSingleton):
pass
class SubSingleton(Singleton):
pass
print(Singleton() == Singleton())
# True
print(SubSingleton() == SubSingleton())
# True
print(Singleton())
# <__main__.Singleton object at 0x10ce84ef0>
print(SubSingleton())
# <__main__.SubSingleton object at 0x10ce84e48>
#Try reversing the generation order
print(SubSingleton())
# <__main__.SubSingleton object at 0x10ce84c18>
print(Singleton())
# <__main__.Singleton object at 0x10ce84cf8>
If you check the above implementation, only one instance is generated from each class, and you can implement it regardless of the generation order, so it is better than the method of overwriting the __new__ ()
method. I think it's safe.
This method was proposed by Alex Martelli, the author of the Python Cookbook. It behaves like a Singleton, but has a different structure, so that __dict__
shares all class instances. This is called Borg, or Monostate.
class Borg(object):
_state = {}
def __new__(cls, *args, **kwargs):
ob = super().__new__(cls, *args, **kwargs)
ob.__dict__ = cls._state
return ob
print(Borg() == Borg())
# False
You can create different instances by creating multiple Borg classes. Still, it can be used as an alternative to the Singleton because multiple instances behave as if they were one. Singleton guarantees one instance, but more importantly, it guarantees one behavior. So Borg, which can guarantee the uniqueness of behavior, is an alternative to Singleton.
However, this method also has a trap. You can solve the problem that you can put in a subclass, but it depends on the implementation of the subclass. For example, if you have overwritten __getattr__
, which is the process for getting attributes with .
in a subclass, Borg will not work properly.
So far, we have introduced various methods, but the easiest method is to prepare a module with functions. That's because Python modules are originally Singleton objects. The above implementation method is required if you are using a framework like Java. So I don't think the Singleton pattern is necessary unless there are special circumstances.
--Expert Python Programming Revised 2nd Edition
Recommended Posts