Summary of several common implementation methods of singletons in python

  • 2020-12-16 06:00:44
  • OfStack

preface

In recent days, I have been looking at the code I wrote before, so I just sort out the things I have used. Singleton mode is often used in daily code work.

So I'm going to put together one of the different singletons that We've implemented before

What is a singleton?

Make sure that a class has only one instance and that you instantiate it and provide that instance to the entire system. This class is called a singleton class, and the singleton pattern is an object creation pattern.

So what is the use of the singleton pattern? Take a common example of the singleton pattern. In the whole operating system, there can only be one instance of the recycle bin on the computer we usually use. The entire system uses the only instance and the recycle bin provides its own instance, so the recycle bin is the application of the singleton pattern.

The way decorators work

This way is also commonly used in the work of 1, it is more convenient to use, the code implementation is as follows


def Singleton(cls):
 _instance = {}

 def _singleton(*args, **kwargs):
  if cls not in _instance:
   _instance[cls] = cls(*args, **kwargs)
  return _instance[cls]

 return _singleton

If we are working on a class that needs a singleton, we can implement it in a way similar to the following:


@Singleton
class A(object):

 def __init__(self, x):
  self.x = x

Personally, I like it that way

Class

There are actually 1 problems to be aware of, let's look at the 1 error code that might occur


class Member(object):

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

At first glance, this class seems to have implemented singletons, but there is a potential problem with writing this if it is multithreaded, especially if there is a time-consuming operation in the initialization object of the current class

For example, the following code:


#! /usr/bin/env python3
# .-*- coding:utf-8 .-*-

import time
import threading
import random


class Member(object):
 
 def __init__(self):
  time.sleep(random.randint(1,3))

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


def task(arg):
 obj = Member.instance()
 print(obj)

for i in range(5):
 t = threading.Thread(target=task, args=[i,])
 t.start()

The execution of this code will result in instantiating multiple objects, which will cause your singleton to fail

Of course it's natural to think of locking as a way to control, so let's change the above code:


#! /usr/bin/env python3
# .-*- coding:utf-8 .-*-


import time
import threading
import random


class Member(object):
 _instance_lock = threading.Lock()

 def __init__(self):
  i = random.randint(1, 3)
  print(i)
  time.sleep(i)

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


def task():
 obj = Member.instance()
 print(obj)

for i in range(5):
 threading.Thread(target=task,).start()

But there's one problem with the above code, which is that every time we call instance after we've instantiated it, we're going to ask for a lock, so this isn't good, so we'll change this part of the code again:


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

This is a good implementation of a single example can be used by multiple threads

conclusion


Related articles: