最近在學到Python中的__new__方法時被弄懵逼了,一開始實在是很難理解,有很多地方想不通(本人強迫症)。最近自己慢慢思索得出了能說服自己的理解:
說__new__方法之前要先提到__init__方法,__init__方法是普遍認為的Python類的構造方法,在我們對類進行實例化的時候,Python解釋器會調用__init__方法對我們在init方法中定義的屬性進行初始化,比如:
class demo(): def __init__(self,arg,kwarg): #定義屬性並初始化 self.arg = arg self.kwarg = kwarg def Output(self): print(self.arg) print(self.kwarg) a = demo("NMSL","WSND") #實例化 a.Output() #調用類中的Output方法
但是其實在Python中,__init__並不是真正的構造函數,准確的說,__new__加__init__才是真正的構造函數,下面詳細說一下__new__方法。
當我們在對類進行實例化的時候,Python解釋器會從__new__方法的返回值中獲取到實例對象的信息,在上面代碼的例子中,我們把類demo進行實例化,對象為a
在這個過程中,類demo是怎么確定它的實例對象是a呢,就是__new__方法返回了這個值,這個值就是a,然后Python解釋器就知道了demo這個類的對象為a,其實這里的返回值最后被Python解釋器告訴__init__方法,init方法中的關鍵字self其實就是這個a,因為self代表的是類的對象。
__new__方法還有另一個用處就是用來實現單例設計模式以及繼承不可變類的時候方便我們定制類:
class newfloat(float): def __new__(cls,value): return super().__new__(cls,round(value,4)) i = newfloat(3.14529) print(i)
這里的思路是用來接收__new__方法的返回值再打印出來。
老實說我在很多文章下面看到過關於__new__方法的應用,都是拿這個舉例子,但是我覺得根本沒有必要這樣,我覺得可以這樣:
class demo(int): def __init__(self,arg): self.arg = arg self.arg = round(self.arg,4) print(self.arg) a = demo(3.1415926)
這樣的寫法同樣能實現功能。
單例設計模式是為了解決一個類有多個對象的時候,多個對象引用同一個內存地址,以減少內存占用的問題。
實現思路:
重寫父類的__new__方法,使每次返回的內存地址引用都為同一個。
class demo(object): ins = None def __new__(cls): if cls.ins == None: cls.ins = super().__new__(cls) return cls.ins
a = demo()
b = demo()
print(a)
print(b)
這里的思路是:定義一個類屬性ins為空值,再在重寫__new__方法時候用if語句進行判斷,如果ins為空則賦給它__new__方法的返回值,當用a去實例化demo時,
實際上是Python解釋器為這次實例化開辟了一片內存空間,a就是引用了這個內存地址。
然后我們再用b去實例化demo,由於ins的值已經被改寫,所以返回的是同樣的之前a對象的內存地址,這樣就成功實現了單例設計模式。
這里必須好好解釋一下__new__方法的參數,在定義new方法時,在參數欄里不能寫self,而是寫cls,self代表的是類的實例,而cls則代表的是類本身。
繼承不可變類型,我們知道Python中的不可變類型有int,str和tuple,但是Python中有這么一個思想:萬物皆對象。也就是說,int,str和tuple這中數據類型其實也是一種對象,拿字符串舉例,我們可以利用count之類的方法直接對字符串進行操作,但是這其實是Python內置類str中定義的方法,我們可以用dir()方法查看類中定義有哪些方法,但是在重寫new方法時最多只能多傳一個參數,這是為什么?
下面時我自己的解釋,我們平時用內置方法比如count()時,一次只能對一個字符串進行操作,理所應當,每次當我們重寫str類中定義的方法時,只能傳入一個值。
其他注意事項:
__init__方法不能有返回值,而__new__方法卻必須有返回值。
如有錯誤,請指正,感激不盡。