您好,登錄后才能下訂單哦!
本篇內容介紹了“Python中__new__方法有什么作用”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
接下來通過實例逐步詳細闡述__ new __ 方法在類初始化過程中是什么樣的存在!
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 A1122517839809864 2517841042504
注:
1)、代碼實例化類過程
一般使用__init__()方法初始化一個類的實例,當代碼中實例化一個類的時候,第一個調用執行的是__new__()方法,當定義的類中沒有重新定義__new__()方法時候,Python會默認調用該父類的__new__()方法來構造該實例,new方法就是先創建一個空間,然后每次創建一個實例化的對象,然后用開辟的空間存放這個實例化對象; 再次創建一個實例化的對象的時候,再用new方法開辟一個空間存放實例化對象。注意只有繼承了object的類才有此方法。
2)、內存地址和對象可相互轉換
#通過_ctypes的api進行對內存地址的對象 import _ctypes obj = _ctypes.PyObj_FromPtr(id(a)) #打印出來通過內存地址尋找到的對象 print(obj)
print(id(a))與 print(id(b))打印出來的都是內存地址(10進制),print(a)與 print(b)返回了類的名稱和對象的地址,但是兩者并不相同。每次實例化類都會創建分配不同的對象地址,因此,代碼實例化類過程中返回類對象的地址引用也就不同。
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))
注:
1)、__init__()方法和__new__()方法區別
__new__()方法用于創建實例,類實例化之前會首先調用,它是class的方法,是個靜態方法。而__init__()方法用戶初始化實例,該方法用在實例對象創建后被調用,它是實例對象的方法,用于設置類實例對象的一些初始值。
如果類中同時出現了__init__()方法和__new__()方法,則先調用__new__()方法后調用__init__()方法。__new__()方法是創建實例的第一步,執行完了需要返回創建的類的實例,否則則報錯,無法執行__init__()方法。其中,__init__()方法將不返回任何信息。
2)、重寫__new__()方法
def __new__(cls, *args, **kwargs): print(cls)# cls 代表的是Solution這個類本身cls.instance = super().__new__(cls)# object().__ new __() print(cls.instance) return cls.instance
super()與object.__new__(cls)都是在調用父類的new方法,必須把父類的new方法返回給函數,才能開辟空間,因此必須添加return。代碼的執行順序是:先執行new方法,然后執行init方法,最后是其它方法。
單例模式最初的定義出現于《設計模式》:“保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。”
單例的使用主要是在需要保證全局只有一個實例可以被訪問的情況,比如系統日志的輸出、操作系統的任務管理器等。
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
注:
1)、單例模式始終只有一個空間,該空間一直重復利用。
首先定義一個類的私有屬性_instance,用來記錄第一個被創建對象的引用,如果cls._instance為None說明該類還沒有實例化過,則實例化該類并返回實例對象。
通過以下數據測試可知,print(obj.name, obj.data)最后打印出來的都是A12,第一次打印"A11"時,屬性為空,執行if語句開辟了一個空間存放該屬性;從 第二次打已經開辟了空間 ,執行else語句,直接返回"A12"到原來的空間中,把前面的蓋數據覆蓋掉。
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()
初始化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)、單例模式另外一種實現方法
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
以上實現了單例模式對象空間的重復利用,但是有時候我們想初始化過程只加載一次,避免頻繁請求浪費系統資源(如數據庫連接請求數據)。
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
注:
1)、單例模式下僅加載一次初始化過程。
這時候我們在類空間中再添加一個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初始化init A11 10 解析完成finish A11 已經存在訪問對象 A11初始化init A11 10 解析完成finish A11 正在創建訪問對象 A12初始化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初始化init A11 10 解析完成finish A11 已經存在訪問對象 A11解析完成finish A11 正在創建訪問對象 A12初始化init A12 20 解析完成finish A12 True A11 A11 A12
注:多例模式下多個實例化對象均只進行一次初始化過程。
重寫__new__方法中每個實例對象創建后綁定初始化_init_db()方法執行一次,后面遇到同一個實例對象將不會發生什么,直接返回已創建的實例對象。從測試結果來看,創建相同的實例對象name="A11"時,第二次將略過初始化數據加載過程,很好的實現了緩存機制。
“Python中__new__方法有什么作用”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。