神技巧!在Python類初始化時進行四種暗箱操作!
Python 的類中,所有以雙下劃線__包起來的方法,叫魔術(shù)方法,魔術(shù)方法在類或?qū)ο蟮哪承┦录l(fā)出后可以自動執(zhí)行,讓類具有神奇的魔力,比如常見的構(gòu)造方法__new__、初始化方法__init__、析構(gòu)方法__del__,今天來聊一聊__new__的妙用,主要分享以下幾點:
__new__和__init__的區(qū)別應(yīng)用1:改變內(nèi)置的不可變類型 應(yīng)用2:實現(xiàn)一個單例 應(yīng)用3:客戶端緩存 應(yīng)用4:不同文件不同的解密方法 應(yīng)用5:Metaclasses
__new__ 和 __init__ 的區(qū)別
1、調(diào)用時機不同:new 是真正創(chuàng)建實例的方法,init 用于實例的初始化,new 先于 init 運行。
2、返回值不同,new 返回一個類的實例,而 init 不返回任何信息。
3、new 是 class 的方法,而 init 是對象的方法。
示例代碼:
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()
上述代碼定義了一個類 A,在調(diào)用 A(1, 2, 3, x=4) 時先執(zhí)行 new,再執(zhí)行 init,等價于:
x?=?A.__new__(A,?1,?2,?3,?x=4)
if?isinstance(x,?A):
????type(x).__init__(x,?1,?2,?3,?x=4)
代碼的運行結(jié)果如下:
new?'__main__.A'>?(1,?2,?3)?{'x':?4}
init?<__main__.A?object?at?0x7fccaec97610>?(1,?2,?3)?{'x':?4}
<__main__.A?object?at?0x7fccaec97610>
===================
new?'__main__.A'>?(1,?2,?3)?{'x':?4}
init?<__main__.A?object?at?0x7fccaec97310>?(1,?2,?3)?{'x':?4}
<__main__.A?object?at?0x7fccaec97310>
new 的主要作用就是讓程序員可以自定義類的創(chuàng)建行為,以下是Python 的構(gòu)造方法?__new__?的常用場景:
應(yīng)用1:改變內(nèi)置的不可變類型
我們知道,元組是不可變類型,但是我們繼承 tuple ,然后可以在 new 中,對其元組的元素進行修改,因為 new 返回之前,元組還不是元組,這在 init 函數(shù)中是無法實現(xiàn)的。比如說,實現(xiàn)一個大寫的元組,代碼如下:
class?UppercaseTuple(tuple):
????def?__new__(cls,?iterable):
????????upper_iterable?=?(s.upper()?for?s?in?iterable)
????????return?super().__new__(cls,?upper_iterable)
????#?以下代碼會報錯,初始化時是無法修改的
????#?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')
應(yīng)用2:實現(xiàn)一個單例
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
應(yīng)用3:客戶端緩存
當客戶端的創(chuàng)建成本比較高時,比如讀取文件或者數(shù)據(jù)庫,可以采用以下方法,同一個客戶端屬于同一個實例,節(jié)省創(chuàng)建對象的成本,這本質(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.
應(yīng)用4:不同文件不同的解密方法
先在腳本所在目錄創(chuàng)建三個文件:plaintext_hello.txt、rot13_hello.txt、otp_hello.txt,程序會根據(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^GLEND
推薦閱讀:
入門:?最全的零基礎(chǔ)學(xué)Python的問題? |?零基礎(chǔ)學(xué)了8個月的Python??|?實戰(zhàn)項目?|學(xué)Python就是這條捷徑
干貨:爬取豆瓣短評,電影《后來的我們》?|?38年NBA最佳球員分析?|? ?從萬眾期待到口碑撲街!唐探3令人失望? |?笑看新倚天屠龍記?|?燈謎答題王?|用Python做個海量小姐姐素描圖?|碟中諜這么火,我用機器學(xué)習(xí)做個迷你推薦系統(tǒng)電影
趣味:彈球游戲? |?九宮格? |?漂亮的花?|?兩百行Python《天天酷跑》游戲!
AI:?會做詩的機器人?|?給圖片上色?|?預(yù)測收入?|?碟中諜這么火,我用機器學(xué)習(xí)做個迷你推薦系統(tǒng)電影
小工具:?Pdf轉(zhuǎn)Word,輕松搞定表格和水印!?|?一鍵把html網(wǎng)頁保存為pdf!|??再見PDF提取收費!?|?用90行代碼打造最強PDF轉(zhuǎn)換器,word、PPT、excel、markdown、html一鍵轉(zhuǎn)換?|?制作一款釘釘?shù)蛢r機票提示器!?|60行代碼做了一個語音壁紙切換器天天看小姐姐!|
年度爆款文案
6).30個Python奇淫技巧集?
點閱讀原文,看200個Python案例!

