Implement the Singleton pattern in Python

Singleton pattern

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.

How to overwrite the __new__ () method

You 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.

How to use a metaclass

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.

Implementation to replace with Borg

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.

Concise implementation

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.

reference

--Expert Python Programming Revised 2nd Edition

Recommended Posts

Implement the Singleton pattern in Python
Singleton pattern in Python
Learn the design pattern "Prototype" in Python
Learn the design pattern "Builder" in Python
Learn the design pattern "Flyweight" in Python
Learn the design pattern "Observer" in Python
Learn the design pattern "Memento" in Python
Learn the design pattern "Command" in Python
Learn the design pattern "Visitor" in Python
Learn the design pattern "Bridge" in Python
Learn the design pattern "Mediator" in Python
Learn the design pattern "Decorator" in Python
Learn the design pattern "Iterator" in Python
Learn the design pattern "Strategy" in Python
Learn the design pattern "Composite" in Python
Learn the design pattern "Singleton" with Python
Learn the design pattern "State" in Python
Learn the design pattern "Adapter" in Python
Singleton pattern in Java
Visitor pattern in Python
Implement recommendations in Python
Implement XENO in python
Implement sum in Python
Implement Traceroute in Python 3
Learn the design pattern "Abstract Factory" in Python
Learn the design pattern "Template Method" in Python
Learn the design pattern "Factory Method" in Python
Find the difference in Python
Implement naive bayes in Python 3.3
Implement ancient ciphers in python
Implement Redis Mutex in Python
Implement extension field in Python
Implement fast RPC in Python
Implement method chain in Python
Implement Dijkstra's Algorithm in python
Implement Slack chatbot in Python
Learn the design pattern "Chain of Responsibility" in Python
Implement the solution of Riccati algebraic equations in Python
Implement stacking learning in Python [Kaggle]
Implement R's power.prop.test function in python
Getting the arXiv API in Python
Python in the browser: Brython's recommendation
Save the binary file in Python
Python Singleton
Hit the Sesami API in Python
Get the desktop path in Python
Get the script path in Python
Hit the web API in Python
I tried to implement the mail sending function in Python
I wrote the queue in Python
Calculate the previous month in Python
Examine the object's class in python
Get the desktop path in Python
Quickly implement REST API in Python
Get the host name in Python
Access the Twitter API in Python
The first step in Python Matplotlib
I wrote the stack in Python
Master the weakref module in Python
Can Python implement the Dependency Inversion Principle (DIP) in the first place?
Load the remote Python SDK in IntelliJ