Several realization methods and optimization details based on singleton pattern in Python

  • 2020-06-23 00:57:58
  • OfStack

The singleton pattern

The singleton pattern (Singleton Pattern) is a common software design pattern whose main purpose is to ensure that only 1 instance of a class exists. Singletons are useful when you want to have only one instance of a class in the system.

For example, the configuration information of a server program is stored in a file, and the client reads the configuration information through an AppConfig class. If there are many places where the contents of the configuration file need to be used while the program is running, that is, many places where instances of AppConfig objects need to be created, this results in multiple instance objects of AppConfig in the system, which can be a serious waste of memory resources, especially if the configuration file is very large. In fact, for a class like AppConfig, we want only one instance object to exist while the program is running.

In Python, there are several ways to implement the singleton pattern

Several ways to implement the singleton pattern

1. Use modules

In fact, the Python module is a natural singleton because the module generates a.pyc file on the first import and loads the.pyc file on the second import without executing the module code again. Therefore, we only need to define the relevant functions and data in a module, and we can get a singleton object. If we really want a singleton class, consider this:


mysingleton.py
class Singleton(object):
 def foo(self):
  pass
singleton = Singleton()

Save the above code in file mysingleton.py, and when you want to use it, import the object in this file directly into another file, which is a singleton object


from a import singleton
 

2. Use classes


class Singleton(object):
 def __init__(self):
 pass
 @classmethod
 def instance(cls, *args, **kwargs):
 if not hasattr(Singleton, "_instance"):
  Singleton._instance = Singleton(*args, **kwargs)
 return Singleton._instance

In general, people think that this completes singleton mode, but this causes problems when using multithreading


class Singleton(object):
 def __init__(self):
 pass
 @classmethod
 def instance(cls, *args, **kwargs):
 if not hasattr(Singleton, "_instance"):
  Singleton._instance = Singleton(*args, **kwargs)
 return Singleton._instance
import threading
def task(arg):
 obj = Singleton.instance()
 print(obj)
for i in range(10):
 t = threading.Thread(target=task,args=[i,])
 t.start()

After the program is executed, the printing results are as follows:


<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>

It doesn't seem to be a problem either, because the execution is too fast. If you have one IO operation in the init method, you will find a problem. Now we simulate it through ES42en.sleep

Let's add the following code to the method above with ___ 46EN__ :


def __init__(self): import time time.sleep(1)

After reexecuting the program, the results are as follows


<__main__.Singleton object at 0x034A3410>
<__main__.Singleton object at 0x034BB990>
<__main__.Singleton object at 0x034BB910>
<__main__.Singleton object at 0x034ADED0>
<__main__.Singleton object at 0x034E6BD0>
<__main__.Singleton object at 0x034E6C10>
<__main__.Singleton object at 0x034E6B90>
<__main__.Singleton object at 0x034BBA30>
<__main__.Singleton object at 0x034F6B90>
<__main__.Singleton object at 0x034E6A90>

Problem! The singleton created in the above manner does not support multithreading

Solution: Lock up! The unlocked part is executed concurrently, while the locked part is executed sequentially, which reduces the speed, but ensures data security


import time
import threading
class Singleton(object):
 _instance_lock = threading.Lock()
 def __init__(self):
 time.sleep(1)
 @classmethod
 def instance(cls, *args, **kwargs):
 with Singleton._instance_lock:
  if not hasattr(Singleton, "_instance"):
  Singleton._instance = Singleton(*args, **kwargs)
 return Singleton._instance

def task(arg):
 obj = Singleton.instance()
 print(obj)
for i in range(10):
 t = threading.Thread(target=task,args=[i,])
 t.start()
time.sleep(20)
obj = Singleton.instance()
print(obj)

The print results are as follows:


<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>

That's about it, but there's one small problem. When the program executes, time.sleep (20), when we instantiate the object, it's already singleton mode, but we still lock it, it's not so good.


@classmethod
 def instance(cls, *args, **kwargs):
 if not hasattr(Singleton, "_instance"):
  with Singleton._instance_lock:
  if not hasattr(Singleton, "_instance"):
   Singleton._instance = Singleton(*args, **kwargs)
 return Singleton._instance

This completes a singleton pattern that supports multiple threads


from a import singleton
 
0

The singleton pattern implemented in this way is limited in use and must be instantiated via obj = Singleton.instance ()

If you use obj=Singleton(), this is not a singleton

3. Based on the method of ___ (recommended and convenient)

From the above example, we can see that when we implement singletons, we need to add locks internally to ensure thread safety

We know that, when we instantiate an object, the method of the class with successie 84en__ (if we haven't written it, the default is to call object. It then executes the method of the class with succinit__ to initialize this object, so we can implement the singleton pattern based on this


from a import singleton
 
1

The print results are as follows:


from a import singleton
 
2

Using the singleton pattern in this way, when instantiating the object later, and when instantiating the object in the same way, obj = Singleton()

4. Implementation based on metaclass

The relevant knowledge


"""
1. Class by type Create, create class time type the __init__ Methods are automatically executed, class ()  perform type the  __call__ methods ( Of the class __new__ methods , Of the class __init__ methods )
2. An object is created by a class __init__ Method automatically executes, object () The implementation of the class  __call__  methods 
"""

Example:


from a import singleton
 
4

Use of metaclasses


from a import singleton
 
5

Implement the singleton pattern


from a import singleton
 
6

Related articles: