Python advanced ----- object-oriented 7.0 (discuss __new__ method and __init__ method in detail)

Table of contents

Foreword:

__init__ method

__new__ method (important!)

1. The calling process of the __new__ method

2. Override the __new__ method

3. __new__ method returns different values

3. Singleton mode


Foreword:

        The __new__() method was initially introduced in the previous issue, but in fact, there are still many contents worth talking about in this method. After learning this method, we can flexibly control the space allocation of our objects (link in the previous issue Advanced Python-----Facing Object 6.0 (binding method [class method, static method] and built-in method)_Python Ouni Sauce's Blog-CSDN Blog ), let's take a look in the next issue!

__init__ method

This method should be familiar to everyone. Yes, this is an initialization method, also called a construction method. When we create a new object, we need to initialize the properties or methods of this object. This method is accompanied by It is executed automatically when the object is created. Here's an example:

class Person(object):
    def __init__(self,name,age):
        self.name=name
        self.age=age  #定义实例化对象的属性
        print('person message')
dick=Person('Dick',18)
#输出结果:person message
print(dick.name,dick.age)
#输出结果:Dick 18

From this example, it is easy to see that the __init__ method is automatically called, and the person message will be automatically output, but before that, there is another method called earlier than the __init__ method, which is to allocate space for the object __new__ method

__new__ method (important!)

1. The calling process of the __new__ method

        The parent class of all classes in Python is the object class, and the object class is also called the super class. When we create an instance object, we first allocate the memory space of the object, and then we can put data into it. At this time, the __new__ method will be used to create this space, and then return this object space to the __init__ method for initialization. That is to say, the __new__ method will return this space and pass it to the self instance parameter in the __init__ method , so the __new__ method is automatically called the same as the __init__ method.

__new__ The method is a built-in static class method . After the python interpreter gets the object reference returned by this method, it passes this reference as the first parameter to the init method, so the new method is executed earlier than the init method   

 Popular understanding:

The above statement may be a bit difficult to understand. Here I will make a metaphor: In daily life, the wooden furniture we use is obtained by processing wood. The __new__ method is also a lumberjack who is responsible for obtaining wood. Then give these woods to the __init__ method, that is, the carpenter, who can use these woods to make all kinds of furniture for us to use

        Some people may ask here, every time I define a class, I don't define the __new__ method, but just define the __init__ method, but how does the memory space come from?

        answer: Note that all classes inherit the object class, so when we write content to a class, we actually rewrite the object class. Both the __init__ method and the __new__ method belong to the object class method, when we go to def __init__(self): , we actually rewrite the __init__ method in the object, but we don’t go to def __new__(cls): That is to say, we don’t rewrite the __new__ method, then The class we defined will retain and call this method, and then return the space of the instantiated object to the __init__ method.

Example:

class A(object):
    def __init__(self,name):
        self.name=name
class B(object):
    object.__new__(object) #当我们没有重写__new__方法,默认继承并且调用object类__new__方法
    def __init__(self,name):
        self.name=name
user=A('dick')
user2=B("hao")
print(user.__sizeof__(),user2.__sizeof__()) #查看占内存
#输出结果:32 32

From the above example, we can see that the effects of class A and class B are exactly the same, but I just wrote out the __new__ method call of class B, so in fact, the __new__ method will automatically inherit and call the object class, we don't need to worry about

Note : The __new__ method is called earlier than the __init__ method

 Look at an example:

class User(object):
    def __init__(self,name):
        self.name=name
        print(f'我的名字是{self.name}')
    def __new__(cls, *args, **kwargs): #对__new__方法重写
        print('这个是__new__方法')
        return object.__new__(cls) #返回这个类对象的空间
jb=User('Dick')
#输出结果:
# 这个是__new__方法
# 我的名字是Dick

It can be seen from the output result that the content in the __new__ method is output first, and then the content in the __init__ method is output, so the __new__ method is executed first

2. Override the __new__ method

        Sometimes we don't like the __new__ method of the parent class, so we will rewrite the method, so what should we pay attention to when rewriting this method?

When we rewrite the __new__ method, we need a return value of the object space index. This return value generally uses the __new__ method of the parent class 

 Precautions:

1. There must be a reference to the object returned (that is, the space of the object)

2. Pay attention to the class of the returned object

Example 1: What happens if no reference to the object is returned?

class A(object):
    def __new__(cls, *args, **kwargs):
        print('重写new方法')
    #这里我没有写返回值会怎么样呢?
    def __init__(self,name):
        print('初始化方法')
        self.name=name
user=A('dick')
print(user)
#输出结果:重写new方法
#         None

Here I have no return value. When I create an instance object, I don’t know where the allocated space is, so I cannot initialize the object, so the __init__ method cannot be executed, so the instance object is not obtained. To the space, that is to say, a space is created, but the space cannot be found, and the data cannot be stored.

Example 2:

class B(object):
    def __init__(self,name):
        self.name=name
        print('初始化方法')
    def __new__(cls, *args, **kwargs):
        print('这是new方法')
        return object.__new__(cls)
user=B('Dick')
print(user.name)
#输出结果:
# 这是new方法
# 初始化方法
# Dick

The __new__ method here has the return value of the object index, and will pass the instantiated object of the current class to the self parameter in the __init__ method, and then initialize it, so this object can get the space.

3. __new__ method returns different values

We have learned the class method before, and we all know that the class method will have a parameter --cls, which means to represent the class itself, and the __new__ method also has a cls parameter, but this parameter does not necessarily represent its own class Meaning, other parent classes can also be passed in. So when this parameter has different incoming values, different situations will occur, and the following will explain them one by one with the code as an example.

Example 1:

class A(object):
    def __init__(self,name):
        self.name=name
    def __new__(cls, *args, **kwargs):
        print('这个是A类重写的方法')
        res=object.__new__(cls)
        return res
class B(A):
    def __init__(self,sound):
        self.sound=sound
user=B('汪汪')
print(user.sound)
#输出结果:
# 这个是A的类
# 汪汪

Here A rewrites the __new__ method in the object class, B inherits A, but B does not rewrite the __new__ method in A, so B directly uses the __new__ method in A and passes in the current class (cls ), and then returns the instantiated space of the current class (cls)

Example 2:

class A(object):
    def __init__(self,name):
        self.name=name
    def __new__(cls, *args, **kwargs):
        print('这个是A类重写的方法')
        res=object.__new__(cls)
        return res
class B(A):
    def __init__(self,sound):
        self.sound=sound
    def __new__(cls, *args, **kwargs):
        print('这个是B类重写的方法')
        res=cls.__new__(cls)  #这里是返回自身的实例对象空间引索
        return res
user=B('汪汪')
print(user.sound)
#结果:报错,进入死循环

 Note: When rewriting the __new__ method, you cannot return the space index of your own class object, otherwise you will enter an infinite loop

Example 3: 

class A(object):
    jk='beauty'
    def __init__(self,name):
        self.name=name
    def __new__(cls, *args, **kwargs):
        res=object.__new__(cls)
        return res
    def fun(self):
        print('大家好!')
class B(A):
    def __init__(self,sound):
        self.sound=sound
    def __new__(cls, *args, **kwargs):
        res=super().__new__(A)  #返回的是类对象A的空间引索
        return res
user=B('汪汪') #实际上这里创建是类A的实例化对象
print(user.jk)  #输出结果:beauty
user.fun()  #输出结果:大家好!
print(user.sound) #报错'A' object has no attribute 'sound'
print(user.name)   #报错'A' object has no attribute 'name'

 This example is to return the space index allocated by class A, but it is different from the type of class B, so it cannot enter the __init__ initialization of class B, which leads to the situation that there is only space but no initialization. When we create a When instantiating the object user, user is an instantiated object of class A, not an instantiated object of class B. (The instantiated object user here does not go through the __init__ initialization of class A and the __init__ initialization of class B)

3. Singleton mode

        The singleton mode is a very common object space allocation mode for computers. For example: when we open the settings window on the computer, when we open the settings window again, the computer will detect whether the window exists, and if it exists, it will not A window will pop up again, that is to say, a new window will be generated only after you fork this window, which can avoid pop-up windows of multiple windows, and also avoid invalid memory occupation (every time a window appears It requires memory), this is called the singleton mode.

 In Python, every time we create an instance object, we need to occupy a memory address. When working on a project, some useless instance objects always occupy this address, causing the program to run slowly. How to implement the singleton mode through the __new__ method in Python? Let's look down.

Benefits of singletons: Save memory

 code show as below:

class Student(object):
    _instance=None  #定义一个类属性,是可以在类中进行调用的
    def __init__(self, name,age):
        self.name=name
        self.age=age
    def __new__(cls, *args, **kwargs):
        if cls._instance is None: #如果这个内存为空的话就进行空间分配
            print('进行空间分配')
            cls._instance=super().__new__(cls)
        return cls._instance
Jack=Student('Jack',18)
John=Student('John',19)
Dick=Student('Dick',18)
print(Jack is Dick is John) #输出:True
print(Jack.name,Jack.age)   #输出:Dick 18
print(John.name,John.age)   #输出:Dick 18
print(Dick.name,Dick.age)   #输出:Dick 18

The above is a singleton mode. These instantiated objects all occupy the same memory address, so the memory addresses are the same, but every time an instantiated object is created, the instantiated object will overwrite an object, and finally The space belongs to the last instantiated object, so it is output Dick 18

 

 Well, the above is the whole content of this issue. Friends, have you learned how to allocate space and memory for objects?

Share a wallpaper every day:

Guess you like

Origin blog.csdn.net/m0_73633088/article/details/129347622