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