The problem: Modern chocolate factories have computer-controlled chocolate boilers. What the boiler does is melt the chocolate and milk together and take it to the next stage to make chocolate bars. Below is a chocolate company boiler controller code, take a closer look, what is wrong with this code?

class ChocolateBoiler(object):

    def __init__(self):
        self.empty = True
        self.boiled = False

    def fill(self):
        Fill the boiler with chocolate and milk mixture
        Boiler must be empty when filling material.
        Once the raw material is filled in, set empty and boiled
        if self.empty:
            self.empty = False
            self.boiled = False

    def drain(self):
        Drain the boiling chocolate and milk
        The boiler must be full and boiling when discharged.
        Set # empty to true
        if not self.empty and self.boiled:
            self.empty = True

    def boil(self):
        Boil the intracranial contents
        The boiler must be full and not boiled when the mixture is cooked
        Once boiled, set the boiled to true
        if not self.empty and not self.boiled:
            self.boiled = TrueCopy the code

As you can see from the code, they add a variety of judgments to prevent bad things from happening. All this judgment is out of the question when there are two instances of ChocolateBoiler. So how do we implement this requirement? At the heart of this problem, we need to determine if the instance already exists and if so, we will not create it again.

_chocolate_boiler_instance = None  # declare instance

def chocolate_boiler(a):
    global _chocolate_boiler_instance  Use global variables

    if _chocolate_boiler_instance is not None: Check if it exists and return if it does
        return _chocolate_boiler_instance
    else:
        # If it doesn't exist, create a new one
        _chocolate_boiler_instance = ChocolateBoiler()
        return _chocolate_boiler_instanceCopy the code

Now when you need to get the chocolate_boiler instance, you just need to call the chocolate_boiler method to get the instance and ensure that you only have one boiler instance at a time.

The pattern that ensures that the ChocolateBoiler class has only one instance and provides a global access point is the singleton pattern.

The singleton pattern

define

Singleton pattern: Ensures that a class has only one instance and provides a global access point.

  • That is, we use the singleton pattern to design a class as a separate instance that we manage, while avoiding other classes from generating instances of their own. And only instances of a singleton are allowed to be obtained through the singleton class.
  • We also provide global access to this instance: when you need an instance, like a class query, it will return a single instance.

implementation

There are several ways to implement the singleton pattern in Python:

Using a metaclass

The Python Cookbook provides an easy-to-use Singleton class that becomes a Singleton when inherited.

# Python 3 code implementation
class Singleton(type):

    def __init__(self, *args, **kwargs):
        self.__instance = None
        super().__init__(*args, **kwargs)

    def __call__(self, *args, **kwargs):
        if self.__instance is None:
            # If __instance does not exist, create a new instance
            self.__instance = super().__call__(*args, **kwargs)
            return self.__instance
        else:
            If it exists, return it directly
            return self.__instance


class Spam(metaclass=Singleton):

    def __init__(self):
        print('Creating Spam')

a = Spam()
b = Spam()

print(a is b)  # this output is TrueCopy the code

Metaclass controls the creation of aclass. Metaclass does three things:

  • Interception class creation
  • Modify the class definition
  • Returns the modified class

In the example, we construct a Singleton metaclass and use the Call method to simulate the behavior of the function. When a class Spam is constructed and its metaclass is set to Singleton, the following behavior occurs when the class object Spam is created:

Spam = Singleton(name,bases,class_dict). Spam is an instance of the Singleton class.

When an instance of Spam is created, Spam()=Singleton(name,bases,class_dict)()=Singleton(name,bases,class_dict).call(), This points all instances of Spam to Spam’s attribute __instance.

usenew

We can use new to control the instance creation process as follows:

class Singleton(object):

    __instance = None

    def __new__(cls, *args, **kw):
        if not cls.__instance:
            cls.__instance = super().__new__(cls, *args, **kw)
        return cls.__instance

class Foo(Singleton):
    a = 1

one = Foo()
two = Foo()
assert one == two
assert one is two
assert id(one) == id(two)Copy the code

The new method binds an instance of a class to the class attribute instance when it is created. If cls.instance is None, the class is not yet instantiated. After instantiation and binding the instance to cls.instance, each instantiation returns the instance created by the first instantiation. Note that when subclassing from Singleton, do not overload new__.

Use decorators

import functools

def singleton(cls):
    ''' Use class as singleton. '''
    The __new__ method is first assigned to __new_original__
    cls.__new_original__ = cls.__new__

    @functools.wraps(cls.__new__)
    def singleton_new(cls, *args, **kw):
        # Try to fetch __it__ from __dict__
        it =  cls.__dict__.get('__it__')
        if it is not None: If there is a value, the instance has been created
            return it
        # If the instance does not exist, create the instance using __new_original__ and assign the instance to __it__
        cls.__it__ = it = cls.__new_original__(cls, *args, **kw)
        it.__init_original__(*args, **kw)
        return it
    # class replaces the __new__ method with singleton_new
    cls.__new__ = singleton_new
    cls.__init_original__ = cls.__init__
    cls.__init__ = object.__init__

    return cls

#
# Use examples
#
@singleton
class Foo:
    def __new__(cls):
        cls.x = 10
        return object.__new__(cls)

    def __init__(self):
        assert self.x == 10
        self.x = 15


assert Foo().x == 15
Foo().x = 20
assert Foo().x == 20Copy the code

The internal implementation of this method is similar to using __new__ :

  • First, assign the new method to new_original, replace the old new method with singleton_new, define init_original and assign cls.init to init_original
  • Inside the singleton_new method, try fetching it (instance) from dict
  • If the instance does not exist, create it using new_original, assign the instance to IT, and return the instance

The simplest way

Bind the name Singleton to the instance. The Singleton is the only object of its own class.

class singleton(object):
    pass
singleton = singleton()Copy the code

Github.com/gusibi/Meti… This is used to retrieve the global request

Python modules are natural singletons because modules generate.pyc files on the first import, and load.pyc files directly on the second import without executing the module code again. Therefore, we only need to define the related functions and data in a module to obtain a singleton object.

Refer to the link

  • Creating a singleton in Python
  • Python singleton mode
  • Why is init() always called after new()?

Finally, thank your girlfriend for her support.

Welcome to follow (April_Louisa) Buy me a Fanta
Welcome to attention
Buy me a Fanta