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:客戶(hù)端緩存
- 應(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:客戶(hù)端緩存
當(dāng)客戶(hù)端的創(chuàng)建成本比較高時(shí),比如讀取文件或者數(shù)據(jù)庫(kù),可以采用以下方法,同一個(gè)客戶(hù)端屬于同一個(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中的?__init__、__new__?和?__call__示例詳解
- Python中的魔術(shù)方法__new__詳解
- Python中__new__()方法適應(yīng)及注意事項(xiàng)詳解
- python中__new__和__init__的實(shí)現(xiàn)
- python __init__與 __new__的區(qū)別
- Python中class內(nèi)置方法__init__與__new__作用與區(qū)別解析
- 詳解Python中__new__方法的作用
- Python中__new__和__init__的區(qū)別與聯(lián)系
- Python 用__new__方法實(shí)現(xiàn)單例的操作
- python中__new__函數(shù)的具體使用
相關(guān)文章
卸載tensorflow-cpu重裝tensorflow-gpu操作
這篇文章主要介紹了卸載tensorflow-cpu重裝tensorflow-gpu操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-06-06
Keras 中Leaky ReLU等高級(jí)激活函數(shù)的用法
這篇文章主要介紹了Keras 中Leaky ReLU等高級(jí)激活函數(shù)的用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07
python中通過(guò)selenium簡(jiǎn)單操作及元素定位知識(shí)點(diǎn)總結(jié)
在本篇文章里小編給大家整理的是關(guān)于python中通過(guò)selenium簡(jiǎn)單操作及元素定位的知識(shí)點(diǎn),有需要的朋友們可以學(xué)習(xí)下。2019-09-09
基于多進(jìn)程中APScheduler重復(fù)運(yùn)行的解決方法
今天小編就為大家分享一篇基于多進(jìn)程中APScheduler重復(fù)運(yùn)行的解決方法,具有很好的價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-07-07
python中fastapi設(shè)置查詢(xún)參數(shù)可選或必選
這篇文章主要介紹了python中fastapi設(shè)置查詢(xún)參數(shù)可選或必選,文圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值需要的小伙伴可以參考一下2022-06-06
python統(tǒng)計(jì)文本字符串里單詞出現(xiàn)頻率的方法
這篇文章主要介紹了python統(tǒng)計(jì)文本字符串里單詞出現(xiàn)頻率的方法,涉及Python字符串操作的相關(guān)技巧,需要的朋友可以參考下2015-05-05

