Python 中類(lèi)的構(gòu)造方法 __New__的妙用
1、概述
python
的類(lèi)中,所有以雙下劃線__包起來(lái)的方法,叫魔術(shù)方法,魔術(shù)方法在類(lèi)或?qū)ο蟮哪承┦录l(fā)出后可以自動(dòng)執(zhí)行,讓類(lèi)具有神奇的魔力,比如常見(jiàn)的構(gòu)造方法__new__
、初始化方法__init__
、析構(gòu)方法__del__
,今天來(lái)聊一聊__new__
的妙用,主要分享以下幾點(diǎn):
- __new__ 和 __init__ 的區(qū)別
- 應(yīng)用1:改變內(nèi)置的不可變類(lèi)型
- 應(yīng)用2:實(shí)現(xiàn)一個(gè)單例
- 應(yīng)用3:客戶端緩存
- 應(yīng)用4:不同文件不同的解密方法
- 應(yīng)用5:Metaclasses
2、__new__ 和 __init__ 的區(qū)別
- 調(diào)用時(shí)機(jī)不同:
new
是真正創(chuàng)建實(shí)例的方法,init
用于實(shí)例的初始化,new
先于init
運(yùn)行。 - 返回值不同,
new
返回一個(gè)類(lèi)的實(shí)例,而init
不返回任何信息。 new
是class
的方法,而init
是對(duì)象的方法。
示例代碼:
class A: def __new__(cls, *args, **kwargs): print("new", cls, args, kwargs) return super().__new__(cls) def __init__(self, *args, **kwargs): print("init", self, args, kwargs) def how_object_construction_works(): x = A(1, 2, 3, x=4) print(x) print("===================") x = A.__new__(A, 1, 2, 3, x=4) if isinstance(x, A): type(x).__init__(x, 1, 2, 3, x=4) print(x) if __name__ == "__main__": how_object_construction_works()
上述代碼定義了一個(gè)類(lèi) A,在調(diào)用 A(1, 2, 3, x=4) 時(shí)先執(zhí)行 new,再執(zhí)行 init,等價(jià)于:
x = A.__new__(A, 1, 2, 3, x=4) if isinstance(x, A): type(x).__init__(x, 1, 2, 3, x=4)
代碼的運(yùn)行結(jié)果如下:
new <class '__main__.A'> (1, 2, 3) {'x': 4} init <__main__.A object at 0x7fccaec97610> (1, 2, 3) {'x': 4} <__main__.A object at 0x7fccaec97610> =================== new <class '__main__.A'> (1, 2, 3) {'x': 4} init <__main__.A object at 0x7fccaec97310> (1, 2, 3) {'x': 4} <__main__.A object at 0x7fccaec97310>
new
的主要作用就是讓程序員可以自定義類(lèi)的創(chuàng)建行為,以下是其主要應(yīng)用場(chǎng)景:
3、應(yīng)用1:改變內(nèi)置的不可變類(lèi)型
我們知道,元組是不可變類(lèi)型,但是我們繼承 tuple
,然后可以在 new
中,對(duì)其元組的元素進(jìn)行修改,因?yàn)?new
返回之前,元組還不是元組,這在 init 函數(shù)中是無(wú)法實(shí)現(xiàn)的。比如說(shuō),實(shí)現(xiàn)一個(gè)大寫(xiě)的元組,代碼如下:
class UppercaseTuple(tuple): def __new__(cls, iterable): upper_iterable = (s.upper() for s in iterable) return super().__new__(cls, upper_iterable) # 以下代碼會(huì)報(bào)錯(cuò),初始化時(shí)是無(wú)法修改的 # def __init__(self, iterable): # print(f'init {iterable}') # for i, arg in enumerate(iterable): # self[i] = arg.upper() if __name__ == '__main__': print("UPPERCASE TUPLE EXAMPLE") print(UppercaseTuple(["hello", "world"])) # UPPERCASE TUPLE EXAMPLE # ('HELLO', 'WORLD')
4、應(yīng)用2:實(shí)現(xiàn)一個(gè)單例
class Singleton: _instance = None def __new__(cls, *args, **kwargs): if cls._instance is None: cls._instance = super().__new__(cls, *args, **kwargs) return cls._instance if __name__ == "__main__": print("SINGLETON EXAMPLE") x = Singleton() y = Singleton() print(f"{x is y=}") # SINGLETON EXAMPLE # x is y=True
5、應(yīng)用3:客戶端緩存
當(dāng)客戶端的創(chuàng)建成本比較高時(shí),比如讀取文件或者數(shù)據(jù)庫(kù),可以采用以下方法,同一個(gè)客戶端屬于同一個(gè)實(shí)例,節(jié)省創(chuàng)建對(duì)象的成本,這本質(zhì)就是多例模式。
class Client: _loaded = {} _db_file = "file.db" def __new__(cls, client_id): if (client := cls._loaded.get(client_id)) is not None: print(f"returning existing client {client_id} from cache") return client client = super().__new__(cls) cls._loaded[client_id] = client client._init_from_file(client_id, cls._db_file) return client def _init_from_file(self, client_id, file): # lookup client in file and read properties print(f"reading client {client_id} data from file, db, etc.") name = ... email = ... self.name = name self.email = email self.id = client_id if __name__ == '__main__': print("CLIENT CACHE EXAMPLE") x = Client(0) y = Client(0) print(f"{x is y=}") z = Client(1) # CLIENT CACHE EXAMPLE # reading client 0 data from file, db, etc. # returning existing client 0 from cache # x is y=True # reading client 1 data from file, db, etc.
6、應(yīng)用4:不同文件不同的解密方法
先在腳本所在目錄創(chuàng)建三個(gè)文件:plaintext_hello.txt、rot13_hello.txt、otp_hello.txt,
程序會(huì)根據(jù)不同的文件選擇不同的解密算法
import codecs import itertools class EncryptedFile: _registry = {} # 'rot13' -> ROT13Text def __init_subclass__(cls, prefix, **kwargs): super().__init_subclass__(**kwargs) cls._registry[prefix] = cls def __new__(cls, path: str, key=None): prefix, sep, suffix = path.partition(":///") if sep: file = suffix else: file = prefix prefix = "file" subclass = cls._registry[prefix] obj = object.__new__(subclass) obj.file = file obj.key = key return obj def read(self) -> str: raise NotImplementedError class Plaintext(EncryptedFile, prefix="file"): def read(self): with open(self.file, "r") as f: return f.read() class ROT13Text(EncryptedFile, prefix="rot13"): def read(self): with open(self.file, "r") as f: text = f.read() return codecs.decode(text, "rot_13") class OneTimePadXorText(EncryptedFile, prefix="otp"): def __init__(self, path, key): if isinstance(self.key, str): self.key = self.key.encode() def xor_bytes_with_key(self, b: bytes) -> bytes: return bytes(b1 ^ b2 for b1, b2 in zip(b, itertools.cycle(self.key))) def read(self): with open(self.file, "rb") as f: btext = f.read() text = self.xor_bytes_with_key(btext).decode() return text if __name__ == "__main__": print("ENCRYPTED FILE EXAMPLE") print(EncryptedFile("plaintext_hello.txt").read()) print(EncryptedFile("rot13:///rot13_hello.txt").read()) print(EncryptedFile("otp:///otp_hello.txt", key="1234").read()) # ENCRYPTED FILE EXAMPLE # plaintext_hello.txt # ebg13_uryyb.gkg # ^FCkYW_X^GLE
到此這篇關(guān)于Python 中類(lèi)的構(gòu)造方法 New的妙用的文章就介紹到這了,更多相關(guān)Python 中類(lèi)的構(gòu)造方法 New內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- python 動(dòng)態(tài)獲取當(dāng)前運(yùn)行的類(lèi)名和函數(shù)名的方法
- python中類(lèi)的輸出或類(lèi)的實(shí)例輸出為<__main__類(lèi)名 object at xxxx>這種形式的原因
- Python異常之常見(jiàn)的Bug類(lèi)型解決方法
- Python如何獲取模塊中類(lèi)以及類(lèi)的屬性方法信息
- python中的3種定義類(lèi)方法
- python實(shí)現(xiàn)層次聚類(lèi)的方法
- Python抽象基類(lèi)的定義與使用方法
- python類(lèi)名和類(lèi)方法cls修改類(lèi)變量的值
相關(guān)文章
python3.6連接mysql數(shù)據(jù)庫(kù)及增刪改查操作詳解
這篇文章主要介紹了python3.6 連接mysql數(shù)據(jù)庫(kù)及增刪改查操作詳解,需要的朋友可以參考下2020-02-02使用Python標(biāo)準(zhǔn)庫(kù)中的wave模塊繪制樂(lè)譜的簡(jiǎn)單教程
這篇文章主要介紹了使用Python標(biāo)準(zhǔn)庫(kù)中的wave模塊繪制樂(lè)譜,涉及到了numpy模塊和坐標(biāo)的可視化運(yùn)用,用到了需要的朋友可以參考下2015-03-03Python實(shí)戰(zhàn)實(shí)現(xiàn)爬取天氣數(shù)據(jù)并完成可視化分析詳解
這篇文章主要和大家分享一個(gè)用Python實(shí)現(xiàn)的小功能:獲取天氣數(shù)據(jù),進(jìn)行可視化分析,帶你直觀了解天氣情況!感興趣的小伙伴可以學(xué)習(xí)一下2022-06-06使用 Python 處理3萬(wàn)多條數(shù)據(jù)只要幾秒鐘
在工作中經(jīng)常遇到大量的數(shù)據(jù)需要整合、去重、按照特定格式導(dǎo)出等情況。這篇文章主要介紹了使用 Python 處理3萬(wàn)多條數(shù)據(jù)只要幾秒鐘的相關(guān)知識(shí),需要的朋友可以參考下2020-01-01Python Numpy數(shù)組擴(kuò)展repeat和tile使用實(shí)例解析
這篇文章主要介紹了Python Numpy數(shù)組擴(kuò)展repeat和tile使用實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12Python使用Dijkstra算法實(shí)現(xiàn)求解圖中最短路徑距離問(wèn)題詳解
這篇文章主要介紹了Python使用Dijkstra算法實(shí)現(xiàn)求解圖中最短路徑距離問(wèn)題,簡(jiǎn)單描述了Dijkstra算法的原理并結(jié)合具體實(shí)例形式分析了Python使用Dijkstra算法實(shí)現(xiàn)求解圖中最短路徑距離的相關(guān)步驟與操作技巧,需要的朋友可以參考下2018-05-05