preamble
What is the purpose of the class constructor __new__ method in Python?
Some Python classes have __ double underscores added before and after some method names and attribute names, which usually belong to Python's special methods and attributes. Special functions can be realized by overriding these methods or calling them directly. Today, let's talk about the application scenarios of the constructor method __new__ the actual program.
We know the common initialization __init__ method, you can rewrite the initialization logic to achieve what you want. Recently, the actual business development process encountered a class of problems such as the implementation of the data resource loading and caching mechanism, the use of magic methods in the constructor method, which __init__ () and __new__ is the object constructor, the reasonable use of the program will be effective in improving performance.
I hope you more combined with their own business needs to deeply understand, flexible use, make the code become more elegant.
I. Introduction to the __new__ method
The __ new __ method is a step-by-step example of what kind of presence the __ new __ method has in the class initialization process!
1. Initialize data loading + parse class instances
class Solution(object): def __init__(self, name=None,data=None): = name = data # Initialize load data self.xml_load() def xml_load(self,data): print("Initialization init",data) def Parser(self): print("Parsing complete. Finish.",) a = Solution(name="A111",data=10) () b = Solution(name="A112",data=20) () # print(a) and print(b) return the name of the class and the address of the object print(a) print(b) # You can view the memory address of a python object using the built-in function id() print(id(a)) print(id(b)) initializationinit 10 Parse completionfinish A111 initializationinit 20 Parse completionfinish A112 <__main__.Solution object at 0x0000024A3AF28D48> <__main__.Solution object at 0x0000024A3B055C48> 2517839809864 2517841042504
Notes:
1, code instantiation class process
generally use the __init__() method to initialize an instance of a class, when the code instantiates a class, the first call to execute the __new__() method, when the definition of the class does not redefine the __new__() method, Python will default to call the parent class of the __new__() method to construct the instance, the new method is to create a space, and then each time you create an instantiated object, and then use the space opened to store the instantiated object; again create an instantiated object, and then use the new method to open up a space to store the instantiated object; again create an instantiated object, then use the new method to open up a space to store the instantiated object; the new method to open up a space to store the instantiated object. The new method creates a space, and then each time you create an instantiated object, you use the opened space to store the instantiated object; when you create an instantiated object again, you use the new method to open up a space to store the instantiated object. Note that only classes that inherit from object have this method.
2. Memory addresses and objects can be converted to each other
#Objects with memory addresses via _ctypes api import _ctypes obj = _ctypes.PyObj_FromPtr(id(a)) # Print out the object found by memory address print(obj)
Both print(id(a)) and print(id(b)) print out memory addresses (in decimal), print(a) and print(b) return the name of the class and the address of the object, but they are not the same. Each time a class is instantiated, a different object address is created and allocated, so the code instantiating the class returns a different reference to the class object's address.
2、Initialization data loading rewrite new method + parse class instance
class Solution: """ Note: The new method is the method that creates space for the instantiated object, now the new method is rewritten and does not return the instantiated object reference to the python interpreter No space storage can be created for the instantiated object, so running the code will report an error. It also doesn't complete the initialization operation. """ def __new__(cls, *args, **kwargs): print("Object creation space") = super().__new__(cls) print() # return # If no instance object reference is returned, the instantiation method will report an error: AttributeError: 'NoneType' object has no attribute 'Parser' def __init__(self,name,data): = name = data self.xml_load() def xml_load(self,data): print("Initialization init", data) def Parser(self): print("Parsing complete. Finish.",) a = Solution("A111",10) () print(id(a))
Notes:
1. Difference between __init__() method and __new__() method
The __new__() method is used to create the instance, it will be called first before the instantiation of the class, it is the method of the class, it is a static method. The __init__() method is used to initialize the instance, which is called after the instance object is created, and it is a method of the instance object, which is used to set some initial values of the class instance object.
If both __init__() and __new__() methods are present in the class, the __new__() method is called first and then the __init__() method is called. The __new__() method is the first step in creating an instance, and after executing it, you need to return the created instance of the class, or else you will report an error and not be able to execute the __init__() method. Among other things, the __init__() method will not return any information.
2. Rewrite the __new__() method
def __new__(cls, *args, **kwargs): print(cls) # cls represents the class Solution itself <class'__ main __.Solution'> = super().__new__(cls) # object().__ new __() print() return
Both super() and object.__new__(cls) are calling the parent class's new method, which must be returned to the function in order to open up space, so return must be added. the code is executed in the following order: the new method is executed first, followed by the init method, and finally the other methods.
II. The single instance model
The original definition of the singleton pattern appeared in Design Patterns, "Ensures that a class has only one instance and provides a global access point to it."
Single instances are used mainly in situations where you need to ensure that only one instance can be accessed globally, such as the output of system logs, the operating system's task manager, and so on.
1, with the new method how to realize the singleton pattern
class Solution: # 1. Record a reference to the first object created, representing a private attribute of the class _instance = None # Static variables Stored in the class namespace def __init__(self,name,data): = name = data self.xml_load() def __new__(cls, *args, **kwargs): # 2. determine if the attributes of the class are empty; for the first object that is not created, we should call the parent class's method to allocate space for the first object if cls._instance == None: # 3. Return object references held in class attributes to python's interpreter cls._instance = object.__new__(cls) # 3 return cls._instance # If cls._instance is not None, return the instantiated instance directly. else: return cls._instance # Must return the address to the new method to give it storage space def xml_load(self,data): print("Initialization init",,data) def Parser(self): print("Parsing complete. Finish.",) a = Solution("A11",10) # The first time an object space address is opened, subsequent creations are done at that address () b = Solution("A12",20) #b overwrites a () print(id(a)) print(id(b)) # Memory addresses, and they're all the same # print() print()
exports
Initialization init A11 10
Parsing completion finish A11
Initialization init A12 10
Parsing complete finish A12
2465140199816
2465140199816
A12
A12
Notes:
1, the single instance model always has only one space, the space has been reused.
First define a private attribute _instance of a class to record a reference to the first object created, and if cls._instance is None indicating that the class has not been instantiated, then instantiate the class and return the instance object.
The following data test can be seen, print (, ) finally printed out are A12, the first print "A11" when the attribute is empty, the implementation of the if statement opens up a space to store the attribute; from the second time to play has opened up space, the implementation of the else statement, directly return to the original space "A12" to the previous cover data to cover off.
def task(id,data): obj = Solution("{0}".format(id), "{0}".format(data)) print(, ) import threading ID=["A11","A12","A13","A14","A12"] DATA=[10,20,30,40,20] for i in range(5): t = (target=task(ID[i],DATA[i]), args=[i, ]) ()
exports
<__main__.Solution object at 0x00000221B2129148>
Initialization init A11 10
A11 10
Initialization init A12 20
A12 20
Initialization init A13 30
A13 30
Initialization init A14 40
A14 40
Initialization init A12 20
A12 20
2. Another way to implement the singleton pattern
def __new__(cls,*args,**kwargs): # hasattr queries the target and determines if there is any, not 1==1 returns False # if statement followed by # not condition as a whole is True, execute = object.... Code # if statement followed by # not The return code is executed when the not condition as a whole is False. if not hasattr(cls,"instance"): # hasattr lookup, the role of judgment = object.__new__(cls) return
2. How to control the class to execute the initialization method only once
The above implementation of the singleton pattern object space reuse, but sometimes we want to initialize the process of loading only once, to avoid frequent requests to waste system resources (such as database connection request data).
class Solution: # Define class variables # Record a reference to the first object created, representing a private attribute of the class _instance = None # Record whether an initialization action has been performed init_flag = False def __init__(self,name,data): = name = data # Use class name to call a class variable, not directly access it. if Solution.init_flag: return self.xml_load() # Modify the markup of class attributes Solution.init_flag = True def __new__(cls, *args, **kwargs): # Determine if the attributes of the class are empty; for the first object that is not created, we should call the methods of the parent class to allocate space for the first object if cls._instance == None: # Return object references held in class attributes to python's interpreter cls._instance = object.__new__(cls) return cls._instance #If cls._instance is not None, return the instantiated instance directly. else: return cls._instance def xml_load(self,data): print("Initialization init",,data) def Parser(self): print("Parsing complete. Finish.",) a = Solution("A11",10) # The address of the first instantiated object at which subsequent creations are made () b = Solution("A12",20) #b overwrites a () print(id(a)) print(id(b)) print() print()
exports
Initialization init A11 10
Parsing completion finish A11
Parsing complete finish A12
2280855720328
2280855720328
A12
A12
Notes:
1. The initialization process is loaded only once in singleton mode.
At this time, we add an init_flag attribute to the class space to record whether the initialization operation has been performed can be achieved by loading the initialization process once. From the results of the above two instantiation process, the object reference address remains unchanged, the result is overwritten by the last instantiation data and the initialization init is printed only once.
2、Single-example mode once the resource loading points of attention
Single-case mode control class only one initialization process is applicable to the process of one-time loading of resources into the cache, for multi-process applications can be implemented using multiple-case mode.
III. Multiple Example Patterns
Multiple instances of object space reference addresses are completely independent, thus maintaining the avoidance of different requests resources are not occupied. Grouping the same object request to the same instance.
class Solution: ## Define a dictionary of objects instantiated by a class, i.e. different instance objects correspond to different object space address references _loaded = {} def __init__(self,name,data): = name = data self.xml_load() def __new__(cls, name,*args): if cls._loaded.get(name) is not None: client = cls._loaded.get(name) print(f"Access object already exists {name}") print(client) return client # Return object references held in class attributes to python's interpreter print(f"Access object being created {name}") client = super().__new__(cls) # Add a space object address reference to the class instance name print(client) cls._loaded[name] = client return client def xml_load(self,data): print("Initialization init",,data) def Parser(self): print("Parsing complete. Finish.",) if __name__ == '__main__': print("Examples of the multiple-case model") a = Solution("A11",10) () b = Solution("A11",10) () c = Solution("A12", 20) () print(f"{a is b}") print() print() print()
Notes:
1、Multi-example mode always has more than one space, different spaces are completely independent.
We define the class instantiation object dictionary in the class space, i.e., we create different instance objects and object space address reference key-value pairs to implement the multiple instance pattern. The class dictionary determines whether the instance object is created or not, saving the cost of creation.
2、Multi-case mode testing process
When creating the same instance object name="A11", the program first searches the instance pool cls._loaded.get(name), if it exists, it directly returns the created instance object space. Multi-case pattern perfectly implements the different access object specific different instantiated object address.
3. Implementation of the buffering mechanism in the multi-case model
Further optimize the initialization process of the multi-case model, such as reading a file or database with only one initialization load.
class Solution: ## Define a dictionary of objects instantiated by a class, i.e. different instance objects correspond to different object space address references _loaded = {} def __new__(cls, name,data,*args): if cls._loaded.get(name) is not None: client = cls._loaded.get(name) print(f"Access object already exists {name}") print(client) return client print(f"Access object being created {name}") # Return object references held in class attributes to python's interpreter client = super().__new__(cls) print(client) # Add a space object address reference to the class instance name cls._loaded[name] = client client._init_db(name,data) return client def _init_db(self,name,data): = name = data self.xml_load() def xml_load(self,data): print("Initialization init",,data) def Parser(self): print("Parsing complete. Finish.",) if __name__ == '__main__': print("Multicase Pattern Example - Caching.") a = Solution("A11",10) () b = Solution("A11",10) () c = Solution("A12", 20) () print(f"{a is b}") print() print() print()
exports
Access object being created A11
<__main__.Solution object at 0x0000024198989148>
Initialization init A11 10
Parsing completion finish A11
Access object already exists A11
<__main__.Solution object at 0x0000024198989148>
Parsing completion finish A11
Access object being created A12
<__main__.Solution object at 0x00000241989891C8>
Initialization init A12 20
Parsing complete finish A12
True
A11
A11
A12
Note: Multiple instantiated objects in multi-case mode are initialized only once.
Rewrite __new__ method in each instance of the object after the creation of the binding initialization_init_db () method is executed once, after the encounter with the same instance of the object will not happen, directly return to the created instance of the object. From the test results, the creation of the same instance object name = "A11", the second time will skip the initialization data loading process, a good implementation of the caching mechanism.
summarize
This article combines the project background describes in detail the __new__ method to implement the singleton pattern and multi-case pattern and the implementation of the caching mechanism!
1. The __new__ method is automatically called when the class creates an instance.
2. Instances are created in the class through the __ new __ method inside the class.
3. First call the __new__ method to create an instance, then call the __ init __ method to initialize the instance.
4. __new__ method, followed by the parentheses inside the cls represents the class itself.
5, __new__ method, determine the class attribute is empty to open up space, otherwise reuse the original address.
More special methods such as 1. self-description method: __repr__ 2. destruct method: __del__ 3. list the names of all attributes (including methods) of an object: __dir__ 4. __dict__ attribute: view the dictionary consisting of the names and values of all attributes inside the object 5. __getattr__ \ __setattr__ and so on.
Of course, there is also the __new__ method of the metaclass class, which allows you to dynamically modify a group of classes in a program. This feature is very useful when developing some basic frameworks, where you can use metaclass to add methods to a group of classes that need general-purpose functionality.
Above is a detailed explanation of the role of the __new__ method in Python in detail, more information about Python __new__ please pay attention to my other related articles!