第041講:魔法方法:構造和析構
#搬運自FishC論壇,該系列已完結,共有00~96節,本人學習過程中的記錄等。
#FishC論壇:http://bbs.fishc.com/forum.php
#小甲魚課程規劃帖:http://bbs.fishc.com/thread-1053-1-1.html 此教程適合完全零基礎的朋友學習,
課堂筆記
-魔法方法總是被雙下劃線包圍,例如:__init__
-魔法方法是面向物件的Python的一切,如果你不知道魔法方法,說明你還沒意識到面向物件的Python的強大。(不是平時寫的Python指令碼)
-魔法方法的“魔力”體現在他們總能在適當的時候被自動呼叫
__init__(self,[, ...])
這個就是其他語言的構造的方法 。也就是類在例項化物件的時候,首先會呼叫的一個方法。
需求!舉個例子,假設我們現在要定義一個矩形類,在 定義的時候我們需要它有長和寬,長和寬這2個引數就在例項化的時候傳入2個引數;在例項化的時候它會呼叫這個__init__方法,所以我們在這裡才需要對__init__方法進行重寫,因為預設它是沒有引數的,需要傳入兩個引數,這就叫做“需求”。self.x是類例項化之後的例項物件的一個區域性變數,而右邊的x是傳入的引數x。self代表類的例項,而非類
>>> class Rectangle: def __init__(self, x, y): self.x = x self.y = y def getPeri(self): return (self.x + self.y) * 2 def getArea(self): return self.x * self.y >>> rect = Rectangle(3, 4) >>> rect.getPeri() 14 >>> rect.getArea() 12 >>>
這裡主要,__init__的返回值一定是返回一個 None,
>>> class A: def __init__(self): return "A for A.Cup" >>> a = A() Traceback (most recent call last): File "<pyshell#4>", line 1, in <module> a = A() TypeError: __init__() should return None, not 'str' >>>
其實__init__()並不是例項化物件時第一個被呼叫的魔法方法。第一個被呼叫的應該是這個__new__(cls[, ...])。它跟其它的魔法方法不同,第一個引數不是self,而是class,也就是這個類在__init__()之前被呼叫,如果後面有引數,那後邊的引數會原封不動地傳給這個__init__()方法。(new在記憶體給物件開盤儲存空間地址,init初始化物件屬性,str直接呼叫物件返回內容)
那new方法需要一個示例物件作為返回值,它需要返回一個物件。通常是返回cls 這個類的示例物件。(new方法只接受 cls 作為它的第一個引數,__init__一個引數是self,因為呼叫__new__之前連示例都還沒有,因此那時根本沒有self的存在)
python3中類的重點與難點:__new__方法與__init__方法,來自輝輝咯連結:https://blog.csdn.net/qq_41020281/article/details/79638370
通俗的講解Python中的__new__()方法,來自SJ2050,原文出處連結:https://blog.csdn.net/sj2050/article/details/81172022
>>> class CapStr(str): def __new__(cls, string): string = string.upper() return str.__new__(cls, string) >>> a = CapStr("I love FishC.com!") >>> a 'I LOVE FISHC.COM!' #解釋:我們這個CapStr類是繼承於str這個類的,而這個str類是一個不可改變的型別,(字串是一個不可改變的型別),我們就不能用__init__()對它自身進行修改,所以我們要做new的時候把它進行一個替換(string = string.upper()),然後將替換後的呼叫這個str.__new__() return str.__new__(cls, string) #返回的是一個新的str類,還要new一次把它賦值給原來的類。不寫的話會呼叫它基類的那個new #理一下:首先,先宣告一個類叫做CapStr,在裡面重寫魔法方法__new__(),然後在__new__()方法中將傳入的string變成大寫,然後將變成大寫的string交給str類中的__new__方法處理,生成一個字串
class A: pass >>> class B(A): def __new__(cls): print("__new__方法被執行") return super().__new__(cls) def __init__(self): print("__init__方法被執行") >>> b = B() __new__方法被執行 __init__方法被執行 >>>
__del__(self)
析構器,當物件將要被銷燬時,這個方法就會自動的被呼叫,但一定要注意的是,並非說是。這個方法是當垃圾回收機制的時候,這時候才會呼叫這個物件的del方法。並不是發生del就會觸發del方法,當這個物件生成後,所有對它的引用都被刪除後,才會啟動這個垃圾回收機制,銷燬物件時就會呼叫__del__()方法。
>>> class C: def __init__(self): print('我是__init__方法,我被呼叫了。。。') def __del__(self): print('我是__del__方法,我被呼叫了。。。') >>> c1 = C() 我是__init__方法,我被呼叫了。。。 >>> c2 = c1 >>> c3 = c2 >>> del c3 >>> del c2 >>> del c1 我是__del__方法,我被呼叫了。。。 >>>
課後習題
測試題:
0. 是哪個特徵讓我們一眼就能認出這貨是魔法方法?
前後有兩個下劃線包圍的時候,就是魔法方法。
答:魔法方法總是被雙下劃線包圍,例如 __init__
1. 類例項化物件所呼叫的第一個方法是什麼?
__new__()這個方法。
答:__new__ 是在一個物件例項化的時候所呼叫的第一個方法。它跟其他魔法方法不同,它的第一個引數不是 self 而是這個類(cls),而其他的引數會直接傳遞給 __init__ 方法的。
2. 什麼時候我們需要在類中明確寫出 __init__ 方法?
當我們有“需求”的時候,就是我們需要初始化一些引數、函式時。
答:當我們的例項物件需要有明確的初始化步驟的時候,你可以在 __init__ 方法中部署初始化的程式碼。例子:
# 我們定義一個矩形類,需要長和寬兩個引數,擁有計算周長和麵積兩個方法。 # 我們需要物件在初始化的時候擁有“長”和“寬”兩個引數,因此我們需要重寫__init__方法 # 因為我們說過,__init__方法是類在例項化成物件的時候首先會呼叫的一個方法,大家可以理解嗎? class Rectangle: def __init__(self, x, y): self.x = x self.y = y def getPeri(self): return (self.x + self.y) * 2 def getArea(self): return self.x * self.y >>> rect = Rectangle(3, 4) >>> rect.getPeri() 14 >>> rect.getArea() 12
3. 請問下邊程式碼存在什麼問題?
class Test: def __init__(self, x, y): return x + y
__init__()這個方法是不需要返回值的,這裡 報typeerror吧
答:程式設計中需要注意到 __init__ 方法的返回值一定是None,不能是其它!
4. 請問 __new__ 方法是負責什麼任務?
複製給init建立一些需要的引數、方法;或者說是修改類的方法,new是負責買材料、零部件的,init負責製造東西
答:__new__ 方法主要任務時返回一個例項物件,通常是引數 cls 這個類的例項化物件,當然你也可以返回其他物件。R
5. __del__ 魔法方法什麼時候會被自動呼叫?
當回收機制啟動的時候,要銷燬一個變數時候,這個類的del方法就會自動呼叫。
答:如果說 __init__ 和 __new__ 方法是物件的構造器的話,那麼 Python 也提供了一個析構器,叫做 __del__ 方法。當物件將要被銷燬的時候,這個方法就會被呼叫。"9Dx}XV
^+2 ,{Z
但一定要注意的是,並非 del x 就相當於自動呼叫 x.__del__(),__del__ 方法是當垃圾回收機制回收這個物件的時候呼叫的。
動動手
0. 小李做事常常丟三落四的,寫程式碼也一樣,常常打開了檔案又忘記關閉。你能不能寫一個 FileObject 類,給檔案物件進行包裝,從而確認在刪除物件時檔案能自動關閉?
要用到del方法嗎,還是說new一個新的類方法。
答:只要靈活搭配 __init__ 和 __del__ 魔法方法,即可做到收放自如。
class FileObject: '''給檔案物件進行包裝從而確認在刪除時檔案流關閉''' def __init__(self, filename='sample.txt'): #讀寫模式開啟一個檔案 self.new_file = open(filename, 'r+') def __del__(self): self.new_file.close() del self.new_file
1. 按照以下要求,定義一個類實現攝氏度到華氏度的轉換(轉換公式:華氏度 = 攝氏度*1.8+32)
要求:我們希望這個類儘量簡練地實現功能,如下:
>>> print(C2F(32)) 89.6
答:為了儘量簡練地實現功能,我們採取了“偷龍轉鳳”的小技巧。在類進行初始化之前,通過“掉包” arg 引數,讓例項物件直接返回計算後的結果。
class C2F(float): "攝氏度轉換為華氏度" def __new__(cls, arg=0.0): return float.__new__(cls, arg * 1.8 + 32)
2. 定義一個類繼承於 int 型別,並實現一個特殊功能:當傳入的引數是字串的時候,返回該字串中所有字元的 ASCII 碼的和(使用 ord() 獲得一個字元的 ASCII 碼值)。
實現如下:
>>> print(Nint(123)) 123 >>> print(Nint(1.5)) 1 >>> print(Nint('A')) 65 >>> print(Nint('FishC')) 461
# 當傳入的引數是字串的時候,返回該字串中所有字元的 ASCII 碼的和(使用 ord() 獲得一個字元的 ASCII 碼值)。 class Nint(int): def __new__(cls, arg = 0): if isinstance(arg, str): total = 0 for each in arg: total += ord(each) arg = total return int.__new__(cls, arg)