Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Artikel yang menerangkan secara terperinci peranan kaedah __new__ dalam Python

Artikel yang menerangkan secara terperinci peranan kaedah __new__ dalam Python

WBOY
WBOYke hadapan
2023-04-12 22:46:051947semak imbas

Artikel yang menerangkan secara terperinci peranan kaedah __new__ dalam Python

Prakata

Apakah fungsi kaedah __new__, pembina kelas dalam Python?

Sesetengah nama kaedah dan nama atribut dalam kelas Python didahului dan diikuti dengan __ garis bawah berganda biasanya kaedah dan atribut khas. Laksanakan fungsi khas dengan mengatasi kaedah ini atau memanggil kaedah ini secara langsung. Hari ini mari kita bincangkan tentang senario aplikasi kaedah pembinaan __new__ dalam program sebenar.

Kami mengetahui kaedah __init__ pemula biasa dan boleh menulis semula untuk melaksanakan logik pemulaan yang kami mahukan. Baru-baru ini, kami telah menghadapi sejenis masalah dalam proses pembangunan perniagaan sebenar, seperti pelaksanaan pemuatan sumber data dan mekanisme caching Kaedah pembinaan dalam kaedah ajaib digunakan Antaranya, __init__() dan __new__ ialah pembina daripada objek. Penggunaan yang munasabah akan meningkatkan prestasi program dengan berkesan.

Saya harap semua orang dapat memahami dengan mendalam dan menggunakannya secara fleksibel berdasarkan keperluan perniagaan mereka sendiri untuk menjadikan kod itu lebih elegan.

1. Pengenalan kepada kaedah __new__

Seterusnya, kami akan secara beransur-ansur menghuraikan kewujudan __ kaedah __ baharu dalam proses permulaan kelas melalui contoh!

1. Pemuatan data awal + contoh kelas penghuraian

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

Nota:

1), proses kelas instantiation kod

Secara amnya gunakan __init__ () kaedah memulakan instance kelas Apabila kelas dijadikan instantiated dalam kod, panggilan pertama dilaksanakan ialah kaedah __new__() Apabila kaedah __new__() tidak ditakrifkan semula dalam kelas yang ditentukan, Python akan secara lalai memanggil __new__(. ) kaedah kelas induk untuk membina contoh Kaedah baharu mula-mula mencipta ruang, kemudian mencipta objek instantiated setiap kali, dan kemudian menggunakan ruang terbuka untuk menyimpan objek instantiated semula Apabila mencipta objek, gunakan kaedah baharu untuk membuka ruang untuk menyimpan objek yang di-instantiate. Ambil perhatian bahawa hanya kelas yang mewarisi objek mempunyai kaedah ini.

2), alamat memori dan objek boleh ditukar antara satu sama lain

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

cetak(id(a)) dan print(id(b)) mencetak alamat memori (10- digit (Sistem), print(a) dan print(b) mengembalikan nama kelas dan alamat objek, tetapi ia tidak sama. Setiap kali kelas dijadikan instantiat, alamat objek berbeza dicipta dan diperuntukkan Oleh itu, rujukan alamat objek kelas dikembalikan semasa proses instantiasi kod juga berbeza.

2. Mulakan pemuatan data dan tulis semula kaedah baharu + hurai contoh kelas

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

Nota:

1), perbezaan antara kaedah __init__() dan __new__() kaedah

Kaedah __new__() digunakan untuk mencipta contoh. Ia akan dipanggil dahulu sebelum kelas dijadikan instantiated. Ia adalah kaedah kelas dan kaedah statik. Kaedah __init__() digunakan untuk memulakan contoh Kaedah ini dipanggil selepas objek contoh dibuat. Ia adalah kaedah objek contoh dan digunakan untuk menetapkan beberapa nilai awal objek contoh kelas.

Jika kedua-dua kaedah __init__() dan kaedah __new__() muncul dalam kelas, kaedah __new__() akan dipanggil dahulu dan kemudian kaedah __init__(). Kaedah __new__() ialah langkah pertama dalam mencipta contoh Selepas pelaksanaan, contoh kelas yang dibuat perlu dikembalikan. Antaranya, kaedah __init__() tidak akan mengembalikan sebarang maklumat.

2), menulis semula kaedah __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() dan object.__new__(cls) kedua-duanya memanggil kaedah baharu kelas induk dan kelas induk The new kaedah kembali ke fungsi untuk membuka ruang, jadi pulangan mesti ditambah. Perintah pelaksanaan kod ialah: pertama laksanakan kaedah baharu, kemudian laksanakan kaedah init, dan akhirnya kaedah lain.

2. Corak Singleton

Takrifan asal corak tunggal muncul dalam "Corak Reka Bentuk": "Pastikan kelas hanya mempunyai satu contoh dan sediakan pusat akses global untuk mengaksesnya."

Penggunaan singleton adalah terutamanya apabila perlu untuk memastikan bahawa hanya satu contoh boleh diakses secara global, seperti output log sistem, pengurus tugas sistem pengendalian, dsb.

1. Bagaimana untuk melaksanakan mod tunggal menggunakan kaedah baharu?

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

Nota:

1). Mod tunggal sentiasa mempunyai satu ruang sahaja dan ruang ini sentiasa digunakan semula.

Mula-mula tentukan atribut persendirian _instance kelas untuk merekodkan rujukan objek pertama yang dicipta Jika cls._instance adalah Tiada, ini bermakna kelas itu belum dibuat instantiate lagi, instantiate kelas dan kembalikan objek instance. .

Melalui ujian data berikut, dapat dilihat bahawa cetakan akhir cetakan(obj.name, obj.data) ialah A12 Apabila "A11" dicetak buat kali pertama, atribut tersebut kosong. dan ruang dibuka dengan melaksanakan pernyataan if Simpan atribut ini sejak kali kedua, laksanakan pernyataan else, terus kembalikan "A12" ke ruang asal, dan tulis ganti data penutup sebelumnya.

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), satu lagi kaedah pelaksanaan mod tunggal

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. Bagaimana untuk mengawal kelas untuk hanya melaksanakan kaedah permulaan sekali?

Perkara di atas menyedari penggunaan semula ruang objek mod tunggal, tetapi kadangkala kami mahu memuatkan proses pemula hanya sekali untuk mengelakkan permintaan kerap yang membazirkan sumber sistem (seperti data permintaan sambungan pangkalan data).

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

Nota:

1), hanya muatkan proses pemula sekali dalam mod tunggal.

这时候我们在类空间中再添加一个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为某一批需要通用功能的类添加方法,若有合适实例场景后续分享。

Atas ialah kandungan terperinci Artikel yang menerangkan secara terperinci peranan kaedah __new__ dalam Python. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:51cto.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam