Heim  >  Artikel  >  Backend-Entwicklung  >  Ein Artikel, der die Rolle der __new__-Methode in Python ausführlich erklärt

Ein Artikel, der die Rolle der __new__-Methode in Python ausführlich erklärt

WBOY
WBOYnach vorne
2023-04-12 22:46:051950Durchsuche

Ein Artikel, der die Rolle der __new__-Methode in Python ausführlich erklärt

Vorwort

Welche Funktion hat die __new__-Methode des Klassenkonstruktors in Python?

Einige Methodennamen und Attributnamen in Python-Klassen werden durch __ doppelte Unterstriche vorangestellt. Solche Methoden und Attribute gehören normalerweise zu den speziellen Methoden und Attributen von Python. Implementieren Sie spezielle Funktionen, indem Sie diese Methoden überschreiben oder diese Methoden direkt aufrufen. Lassen Sie uns heute über die Anwendungsszenarien der Konstruktionsmethode __new__ in tatsächlichen Programmen sprechen.

Wir kennen die allgemeine Initialisierungsmethode __init__ und können sie umschreiben, um die gewünschte Initialisierungslogik zu implementieren. Vor kurzem sind wir im eigentlichen Geschäftsentwicklungsprozess auf ein Problem gestoßen, z. B. bei der Implementierung des Mechanismus zum Laden und Zwischenspeichern von Datenressourcen. Unter diesen sind __init__() und __new__ die Konstruktoren Eine sinnvolle Nutzung des Objekts führt zu einer effektiven Verbesserung der Programmleistung.

Ich hoffe, dass jeder es genau verstehen und flexibel entsprechend seinen eigenen Geschäftsanforderungen nutzen kann, um den Code eleganter zu gestalten.

1. Einführung in die __new__-Methode

Als nächstes erklären wir anhand von Beispielen ausführlich, wie die __new__-Methode im Klasseninitialisierungsprozess existiert!

1. Laden von Daten und Parsen von Klasseninstanzen

class Solution(object):
 def __init__(self, name=None,data=None):
 self.name = name
 self.data = data
 #初始化加载数据
 self.xml_load(self.data)
 def xml_load(self,data):
 print("初始化init",data)
 def Parser(self):
 print("解析完成finish",self.name)
a = Solution(name="A111",data=10)
a.Parser()
b = Solution(name="A112",data=20)
b.Parser()
# print(a)与 print(b)返回了类的名称和对象的地址
print(a)
print(b)
# 可以使用内置函数id()查看python对象的内存地址
print(id(a))
print(id(b))
初始化init 10
解析完成finish A111
初始化init 20
解析完成finish A112
<__main__.Solution object at 0x0000024A3AF28D48>
<__main__.Solution object at 0x0000024A3B055C48>
2517839809864
2517841042504

Hinweis:

1) Code-Instanziierungsprozess

Verwenden Sie im Allgemeinen die Methode __init__(), um eine Instanz einer Klasse zu initialisieren Der erste ausgeführte Aufruf ist die Methode __new__(). Wenn die Methode __new__() in der definierten Klasse nicht neu definiert wird, ruft Python standardmäßig die Methode __new__() der übergeordneten Klasse auf, um die Instanz zu erstellen Erstellen Sie einen Raum und erstellen Sie dann jedes Mal ein instanziiertes Objekt. Verwenden Sie dann den geöffneten Raum zum Speichern des instanziierten Objekts. Wenn Sie erneut ein instanziiertes Objekt erstellen, verwenden Sie die neue Methode, um einen Raum zum Speichern des instanziierten Objekts zu öffnen. Beachten Sie, dass nur Klassen, die Objekte erben, über diese Methode verfügen.

2), Speicheradressen und Objekte können ineinander umgewandelt werden

#通过_ctypes的api进行对内存地址的对象
import _ctypes
obj = _ctypes.PyObj_FromPtr(id(a))
#打印出来通过内存地址寻找到的对象
print(obj)

print(id(a)) und print(id(b)) drucken die Speicheradresse aus (dezimal), print(a) und print( b ) Der Name der Klasse und die Adresse des Objekts werden zurückgegeben, sind jedoch nicht identisch. Bei jeder Instanziierung einer Klasse wird eine andere Objektadresse erstellt und zugewiesen. Daher ist auch die Adressreferenz des während des Code-Instanziierungsprozesses zurückgegebenen Klassenobjekts unterschiedlich.

2. Datenladen initialisieren und neue Methode + Klasseninstanz analysieren

class Solution:
 """
 注:new方法是为实例化对象创建空间的方法,现在new方法被改写,没有将实例化对象引用返回给python的解释器
 无法为实例化对象创建空间存储,所以运行代码会报错。也没有完成初始化操作。
 """
 def __new__(cls, *args, **kwargs):
 print("对象创建空间")
 cls.instance = super().__new__(cls)
 print(cls.instance)
 # return cls.instance #若未返回实例对象引用,实例化方法将报错:AttributeError: 'NoneType' object has no attribute 'Parser'
 def __init__(self,name,data):
 self.name = name
 self.data = data
 self.xml_load(self.data)
 def xml_load(self,data):
 print("初始化init", data)
 def Parser(self):
 print("解析完成finish",self.data)
a = Solution("A111",10)
a.Parser()
print(id(a))

Hinweis:

1) Der Unterschied zwischen der Methode __init__() und der Methode __new__()

__new__() wird zum Erstellen von Instanzen und Klassen verwendet Instanziierung Es wird zuerst aufgerufen. Es handelt sich um eine Klassenmethode und eine statische Methode. Die Methode __init__() wird zum Initialisieren der Instanz verwendet. Diese Methode wird nach der Erstellung des Instanzobjekts aufgerufen. Sie ist eine Methode des Instanzobjekts und wird zum Festlegen einiger Anfangswerte des Klasseninstanzobjekts verwendet.

Wenn sowohl die Methode __init__() als auch die Methode __new__() in der Klasse vorkommen, wird zuerst die Methode __new__() und dann die Methode __init__() aufgerufen. Die Methode __new__() ist der erste Schritt beim Erstellen einer Instanz. Nach der Ausführung muss die Instanz der erstellten Klasse zurückgegeben werden. Andernfalls wird ein Fehler gemeldet und die Methode __init__() kann nicht ausgeführt werden. Unter anderem gibt die Methode __init__() keine Informationen zurück.

2), das Umschreiben der Methode __new__()

def __new__(cls, *args, **kwargs):
 print(cls)# cls 代表的是Solution这个类本身<class'__ main __.Solution'>
 cls.instance = super().__new__(cls)# object().__ new __()
 print(cls.instance)
 return cls.instance

super() und object.__new__(cls) rufen beide die neue Methode der übergeordneten Klasse auf, die zum Öffnen an die Funktion zurückgegeben werden muss Platzbedarf. Daher muss Return hinzugefügt werden. Die Ausführungsreihenfolge des Codes lautet: Führen Sie zuerst die neue Methode aus, dann die Init-Methode und schließlich andere Methoden.

2. Singleton-Muster

Die ursprüngliche Definition des Singleton-Musters erschien in „Design Pattern“: „Stellen Sie sicher, dass eine Klasse nur eine Instanz hat und stellen Sie einen globalen Zugriffspunkt bereit, um darauf zuzugreifen.“

Die Hauptverwendung von Singleton ist In Situationen, in denen sichergestellt werden muss, dass nur auf eine Instanz der Welt zugegriffen werden kann, wie z. B. die Ausgabe von Systemprotokollen, der Task-Manager des Betriebssystems usw.

1. Wie implementiert man den Singleton-Modus mit einer neuen Methode?

class Solution:
 # 1、记录第一个被创建对象的引用,代表着类的私有属性
 _instance = None # 静态变量 存储在类的命名空间里的
 def __init__(self,name,data):
 self.name = name
 self.data = data
 self.xml_load(self.data)
 def __new__(cls, *args, **kwargs):
 # 2.判断该类的属性是否为空;对第一个对象没有被创建,我们应该调用父类的方法,为第一个对象分配空间
 if cls._instance == None:
 # 3.把类属性中保存的对象引用返回给python的解释器
 cls._instance = object.__new__(cls)# 3
 return cls._instance
 # 如果cls._instance不为None,直接返回已经实例化了的实例对象
 else:
 return cls._instance# 必须把地址返回给new方法,让它有存储空间
 def xml_load(self,data):
 print("初始化init",self.name,data)
 def Parser(self):
 print("解析完成finish",self.name)
a = Solution("A11",10)#第一次开辟一个对象空间地址,后面创建都是在该地址上进行的
a.Parser()
b = Solution("A12",20)#b把a覆盖掉
b.Parser()
print(id(a))
print(id(b))
# 内存地址,而且它们的内存地址都是一样的
print(a.name)
print(b.name)
初始化init A11 10
解析完成finish A11
初始化init A12 10
解析完成finish A12
2465140199816
2465140199816
A12
A12

Hinweis:

1) Der Singleton-Modus hat immer nur ein Leerzeichen und dieses Leerzeichen wird immer wiederverwendet.

Definieren Sie zunächst das private Attribut _instance einer Klasse, um die Referenz des ersten erstellten Objekts aufzuzeichnen. Wenn cls._instance None ist, bedeutet dies, dass die Klasse noch nicht instanziiert wurde. Instanziieren Sie die Klasse und geben Sie das Instanzobjekt zurück.

Durch den folgenden Datentest kann festgestellt werden, dass der endgültige Ausdruck von print(obj.name, obj.data) A12 ist. Wenn „A11“ zum ersten Mal gedruckt wird, ist das Attribut leer Öffnet einen Platz zum Speichern dieses Attributs. Der Platz wurde seit dem zweiten Mal geöffnet. Führen Sie die else-Anweisung aus und geben Sie „A12“ direkt an den ursprünglichen Platz zurück, wobei die vorherigen Cover-Daten überschrieben werden.

def task(id,data):
 obj = Solution("{0}".format(id), "{0}".format(data))
 print(obj.name, obj.data)
import threading
ID=["A11","A12","A13","A14","A12"]
DATA=[10,20,30,40,20]
for i in range(5):
 t = threading.Thread(target=task(ID[i],DATA[i]), args=[i, ])
 t.start()
<__main__.Solution object at 0x00000221B2129148>
初始化init A11 10
A11 10
初始化init A12 20
A12 20
初始化init A13 30
A13 30
初始化init A14 40
A14 40
初始化init A12 20
A12 20

2), eine weitere Implementierungsmethode des Singleton-Modus

def __new__(cls,*args,**kwargs):
 # hasattr查询目标并判断有没有,not1==1返回的是False
 # if语句后面的
 # not 条件整体为True时,执行cls.instance = object....代码
 # if语句后面的
 # not 条件整体为False时,执行return代码
 if not hasattr(cls,"instance"): # hasattr查、判断的作用
 cls.instance = object.__new__(cls)
 return cls.instance

2. Wie kann die Klasse so gesteuert werden, dass sie die Initialisierungsmethode nur einmal ausführt?

Das Obige realisiert die Wiederverwendung des Objektraums im Singleton-Modus, aber manchmal möchten wir den Initialisierungsprozess nur einmal laden, um häufige Anforderungen zu vermeiden, die Systemressourcen verschwenden (z. B. Datenbankverbindungsanforderungsdaten).

class Solution:
 #定义类变量
 # 记录第一个被创建对象的引用,代表着类的私有属性
 _instance = None
 #记录是否执行过初始化动作
 init_flag = False
 def __init__(self,name,data):
 self.name = name
 self.data = data
 #使用类名调用类变量,不能直接访问。
 if Solution.init_flag:
 return
 self.xml_load(self.data)
 # 修改类属性的标记
 Solution.init_flag = True
 def __new__(cls, *args, **kwargs):
 # 判断该类的属性是否为空;对第一个对象没有被创建,我们应该调用父类的方法,为第一个对象分配空间
if cls._instance == None:
 # 把类属性中保存的对象引用返回给python的解释器
 cls._instance = object.__new__(cls)
 return cls._instance
 #如果cls._instance不为None,直接返回已经实例化了的实例对象
 else:
return cls._instance
 def xml_load(self,data):
 print("初始化init",self.name,data)
 def Parser(self):
 print("解析完成finish",self.name)
a = Solution("A11",10)#第一次实例化对象地址,后面创建都是在该地址上进行的
a.Parser()
b = Solution("A12",20)#b把a覆盖掉
b.Parser()
print(id(a))
print(id(b))
print(a.name)
print(b.name)
初始化init A11 10
解析完成finish A11
解析完成finish A12
2280855720328
2280855720328
A12
A12

Hinweis:

1) Laden Sie den Initialisierungsprozess nur einmal im Singleton-Modus.

这时候我们在类空间中再添加一个init_flag属性来记录是否已经执行过初始化操作即可实现加载一次初始化过程。从以上两次实例化过程结果来看,对象引用地址不变,结果被最后一次实例化数据覆盖且初始化init只被打印一次。

2)、单例模式下一次资源加载注意点

单例模式下控制类仅进行一次初始化过程适用于资源一次性加载进缓存的过程,对于多进程应用可采用多例模式实现。

三、多例模式

多个实例对象空间引用地址完全独立,从而保持避免不同请求资源不被占用。将同一个对象请求归为同一个实例。

class Solution:
 ##定义类实例化对象字典,即不同的实例对象对应不同的对象空间地址引用
 _loaded = {}
 def __init__(self,name,data):
 self.name = name
 self.data = data
 self.xml_load(self.data)
 def __new__(cls, name,*args):
 if cls._loaded.get(name) is not None:
 client = cls._loaded.get(name)
 print(f"已经存在访问对象 {name}")
 print(client)
 return client
 # 把类属性中保存的对象引用返回给python的解释器
 print(f"正在创建访问对象 {name}")
 client = super().__new__(cls)
 # 为该类实例name添加一个空间对象地址引用
 print(client)
 cls._loaded[name] = client
 return client
 def xml_load(self,data):
 print("初始化init",self.name,data)
 def Parser(self):
 print("解析完成finish",self.name)
if __name__ == '__main__':
 print("多例模式实例")
 a = Solution("A11",10)
 a.Parser()
 b = Solution("A11",10)
 b.Parser()
 c = Solution("A12", 20)
 c.Parser()
 print(f"{a is b}")
 print(a.name)
 print(b.name)
 print(c.name)

注:

1)、多例模式始终具有多个空间,不同空间完全独立。

我们在类空间中定义类实例化对象字典,即建立不同的实例对象和对象空间地址引用键值对,从而实现多例模式。通过类字典判断实例对象是否创建,节省创建的成本。

2)、多例模式测试过程

当创建相同的实例对象name="A11"时,程序首先在实例池中搜索cls._loaded.get(name),若存在则直接返回已创建的实例对象空间。多例模式完美的实现了不同访问对象具体不同的实例化对象地址。

多例模式实例
正在创建访问对象 A11
<__main__.Solution object at 0x000001C105AA5EC8>
初始化init A11 10
解析完成finish A11
已经存在访问对象 A11
<__main__.Solution object at 0x000001C105AA5EC8>
初始化init A11 10
解析完成finish A11
正在创建访问对象 A12
<__main__.Solution object at 0x000001C105AA5F88>
初始化init A12 20
解析完成finish A12
True
A11
A11
A12

3)、多例模式下缓冲机制的实现

进一步优化多例模式初始化过程,比如读取文件或者数据库时仅进行一次初始化加载。

class Solution:
 ##定义类实例化对象字典,即不同的实例对象对应不同的对象空间地址引用
 _loaded = {}
 def __new__(cls, name,data,*args):
 if cls._loaded.get(name) is not None:
 client = cls._loaded.get(name)
 print(f"已经存在访问对象 {name}")
 print(client)
 return client
 print(f"正在创建访问对象 {name}")
 # 把类属性中保存的对象引用返回给python的解释器
 client = super().__new__(cls)
 print(client)
 # 为该类实例name添加一个空间对象地址引用
 cls._loaded[name] = client
 client._init_db(name,data)
 return client
 def _init_db(self,name,data):
 self.name = name
 self.data = data
 self.xml_load(self.data)
 def xml_load(self,data):
 print("初始化init",self.name,data)
 def Parser(self):
 print("解析完成finish",self.name)
if __name__ == '__main__':
 print("多例模式实例-缓存")
 a = Solution("A11",10)
 a.Parser()
 b = Solution("A11",10)
 b.Parser()
 c = Solution("A12", 20)
 c.Parser()
 print(f"{a is b}")
 print(a.name)
 print(b.name)
 print(c.name)
多例模式实例
正在创建访问对象 A11
<__main__.Solution object at 0x0000024198989148>
初始化init A11 10
解析完成finish A11
已经存在访问对象 A11
<__main__.Solution object at 0x0000024198989148>
解析完成finish A11
正在创建访问对象 A12
<__main__.Solution object at 0x00000241989891C8>
初始化init A12 20
解析完成finish A12
True
A11
A11
A12

注:多例模式下多个实例化对象均只进行一次初始化过程。

重写__new__方法中每个实例对象创建后绑定初始化_init_db()方法执行一次,后面遇到同一个实例对象将不会发生什么,直接返回已创建的实例对象。从测试结果来看,创建相同的实例对象name="A11"时,第二次将略过初始化数据加载过程,很好的实现了缓存机制。

__new__方法总结

本文结合项目背景详细介绍了__new__方法实现单例模式和多例模式以及缓存机制的实现!

1、__new__ 方法是在类创建实例的时候自动调用的。

2、 实例是通过类里面的 __ new __ 方法是在类创建出来的。

3、 先调用__new__ 方法创建实例,再调用 __ init __方法初始化实例。

4、 __new__ 方法,后面的括号里面的cls代表的是类本身。

5、__new__ 方法,判断类属性为空就去开辟空间,否则复用原来的地址。

更多的特殊方法比如1、自我描述方法:__repr__2、析构方法:__del__ 3、列出对象所有属性(包括方法)名:__dir__4、__dict__属性:查看对象内部所有属性名和属性值组成的字典5、__getattr____setattr__等。

当然还有metaclass类的__new__方法,可以动态修改程序中的一批类,这个功能在开发一些基础性的框架时非常有用,可以使用metaclass为某一批需要通用功能的类添加方法,若有合适实例场景后续分享。

Das obige ist der detaillierte Inhalt vonEin Artikel, der die Rolle der __new__-Methode in Python ausführlich erklärt. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:51cto.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen