Python中的super().__init__()用法詳解
基本概念
class SingleConv(nn.Module): def __init__(self): super(SingleConv, self).__init__() self.conv = nn.Conv2d(3, 16, kernel_size=3, padding=1) def forward(self, x): return self.conv(x)
super(SingleConv, self).__init__()
這行代碼用于調(diào)用父類(基類)的初始化方法。具體來說,它讓 SingleConv
類調(diào)用其父類 nn.Module
的 __init__()
方法,確保父類被正確初始化。
這是面向對象編程中繼承機制的重要組成部分,特別是在構建 PyTorch 神經(jīng)網(wǎng)絡模型時尤為關鍵。
語法分解
讓我們逐部分解析這個表達式:
super()
- 這是 Python 內(nèi)置函數(shù),用于返回一個代理對象,該對象將方法調(diào)用委托給父類或兄弟類。super(SingleConv, self)
- 這部分創(chuàng)建了一個代理對象,指向SingleConv
類的父類(在這個例子中是nn.Module
)。第一個參數(shù)指定類本身,第二個參數(shù)通常是類的實例(即self
)。super(SingleConv, self).__init__()
- 通過代理對象調(diào)用父類的__init__()
方法,確保父類的初始化代碼被執(zhí)行。
為什么這很重要?
一般繼承原則
在繼承關系中,子類需要確保父類被正確初始化,因為:
- 父類可能設置了子類依賴的重要屬性和狀態(tài)
- 父類可能執(zhí)行了必要的初始化邏輯
- 如果不調(diào)用父類的初始化方法,繼承鏈就會斷開,子類將無法完全繼承父類的功能
在 PyTorch 中的特殊重要性
在 PyTorch 的 nn.Module
上下文中,調(diào)用 super().__init__()
尤為關鍵,因為:
參數(shù)管理 -
nn.Module
的初始化方法設置了追蹤和管理模型參數(shù)(如卷積層的權重和偏置)的機制模塊注冊 - 它建立了子模塊的注冊系統(tǒng),使 PyTorch 能夠識別模型的層次結構
功能支持 - 它啟用了許多核心功能,包括:
- 參數(shù)遷移(使用
.to(device)
將模型移動到 CPU/GPU) - 模型保存和加載(使用
torch.save()
和torch.load()
) - 訓練和評估模式切換(
.train()
和.eval()
) - 自動求導支持
- 參數(shù)遷移(使用
如果省略會發(fā)生什么?
如果您省略 super().__init__()
調(diào)用,可能會導致:
class SingleConvWithoutSuper(nn.Module): def __init__(self): # 沒有調(diào)用 super().__init__() self.conv = nn.Conv2d(3, 16, kernel_size=3, padding=1) def forward(self, x): return self.conv(x) model = SingleConvWithoutSuper() print(list(model.parameters())) # 可能返回空列表,因為參數(shù)沒有被正確注冊
這樣的模型會出現(xiàn)多種問題:
- 參數(shù)不會被正確注冊和跟蹤
- 無法正常使用
.to(device)
遷移到 GPU - 保存和加載模型時可能丟失參數(shù)
- 梯度可能無法正確傳播
Python 3 的簡化語法
在 Python 3 中,可以使用更簡潔的語法:
class SingleConv(nn.Module): def __init__(self): super().__init__() # 簡化版,等效于 super(SingleConv, self).__init__() self.conv = nn.Conv2d(3, 16, kernel_size=3, padding=1) def forward(self, x): return self.conv(x)
這種寫法功能完全相同,但更簡潔易讀。Python 3 的 super()
不帶參數(shù)時會自動使用當前類和實例。
多重繼承中的作用
在涉及多重繼承的復雜情況下,super()
特別有用。它會按照方法解析順序(MRO)正確調(diào)用父類,避免同一個父類被初始化多次:
class A: def __init__(self): print("A init") class B(A): def __init__(self): super().__init__() print("B init") class C(A): def __init__(self): super().__init__() print("C init") class D(B, C): def __init__(self): super().__init__() print("D init")
當創(chuàng)建 D
的實例時,super()
確保每個父類的 __init__
只被調(diào)用一次,遵循 Python 的 MRO 規(guī)則。
執(zhí)行結果
當我們創(chuàng)建 D
類的實例(如 d = D()
)時,輸出結果為:
A init
C init
B init
D init
這個輸出順序可能看起來有些反直覺,特別是 C init
出現(xiàn)在 B init
之前,盡管在 D
的繼承聲明中 B
是第一個父類。讓我們深入分析這是為什么。
方法解析順序 (MRO)
Python 使用一種稱為方法解析順序(Method Resolution Order, MRO)的機制來確定多重繼承中方法查找的順序。MRO 決定了當調(diào)用 super()
時,Python 應該按照什么順序查找父類的方法。
我們可以通過以下方式查看一個類的 MRO:
print(D.__mro__) # 或者 print(D.mro())
對于我們的例子,D
類的 MRO 是:
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
這意味著 Python 在 D
的實例上查找方法時,會按照 D -> B -> C -> A -> object
的順序搜索。
super() 的工作原理
理解 super()
的關鍵在于:super()
不是簡單地調(diào)用父類的方法,而是調(diào)用 MRO 中當前類之后的下一個類的方法。
當在一個類中使用 super().__init__()
時,Python 會查找 MRO 中當前類之后的下一個類,并調(diào)用其 __init__
方法。這是一個非常強大的機制,尤其是在處理復雜的繼承結構時。
詳細的執(zhí)行流程
讓我們逐步追蹤 D()
創(chuàng)建實例時的執(zhí)行流程:
調(diào)用 D.__init__()
- 執(zhí)行
super().__init__()
- 根據(jù) MRO,
D
之后的類是B
,所以調(diào)用B.__init__()
- 執(zhí)行
進入 B.__init__()
- 執(zhí)行
super().__init__()
- 根據(jù) MRO,
B
之后的類是C
,所以調(diào)用C.__init__()
- 執(zhí)行
進入 C.__init__()
- 執(zhí)行
super().__init__()
- 根據(jù) MRO,
C
之后的類是A
,所以調(diào)用A.__init__()
- 執(zhí)行
進入 A.__init__()
- 打印
"A init"
A.__init__()
執(zhí)行完畢,返回到C.__init__()
- 打印
回到 C.__init__()
- 打印
"C init"
C.__init__()
執(zhí)行完畢,返回到B.__init__()
- 打印
回到 B.__init__()
- 打印
"B init"
B.__init__()
執(zhí)行完畢,返回到D.__init__()
- 打印
回到 D.__init__()
- 打印
"D init"
D.__init__()
執(zhí)行完畢
- 打印
圖解說明
下面是繼承結構和執(zhí)行順序的圖解:
A
/ \
B C
\ /
D
執(zhí)行順序(箭頭表示調(diào)用方向):
D.__init__() → B.__init__() → C.__init__() → A.__init__() ↓ D.__init__() ← B.__init__() ← C.__init__() ← 返回并打印 "A init" ↓ ↓ ↓ ↓ ↓ 打印 "C init" ↓ 打印 "B init" 打印 "D init"
與直接調(diào)用父類方法的對比
為了理解 super()
的價值,讓我們看看如果不使用 super()
而是直接調(diào)用父類的 __init__
方法會發(fā)生什么:
class A: def __init__(self): print("A init") class B(A): def __init__(self): A.__init__(self) # 直接調(diào)用 A.__init__ print("B init") class C(A): def __init__(self): A.__init__(self) # 直接調(diào)用 A.__init__ print("C init") class D(B, C): def __init__(self): B.__init__(self) # 直接調(diào)用 B.__init__ C.__init__(self) # 直接調(diào)用 C.__init__ print("D init")
使用這種方式,創(chuàng)建 D
的實例將輸出:
A init # 從 B.__init__ 調(diào)用 B init A init # 從 C.__init__ 調(diào)用,A 被初始化了兩次! C init D init
可以看到,A.__init__()
被調(diào)用了兩次!這可能導致資源重復分配、狀態(tài)不一致或其他問題。
總結
super(SingleConv, self).__init__()
這行代碼是確保 PyTorch 神經(jīng)網(wǎng)絡模塊正確初始化的關鍵步驟。它調(diào)用父類 nn.Module
的初始化方法,設置必要的內(nèi)部狀態(tài),并啟用 PyTorch 的核心功能。
在 Python 3 中,推薦使用更簡潔的 super().__init__()
語法。無論使用哪種形式,確保在每個繼承自 nn.Module
的類的 __init__
方法中調(diào)用它,這是構建正確功能的 PyTorch 模型的基礎。
簡單來說,這行代碼就像告訴您的類:“在我開始自己的初始化工作之前,請確保我從父類繼承的所有功能都已正確設置好。”
到此這篇關于Python中的super().__init__()用法詳解的文章就介紹到這了,更多相關Python中super().__init__()內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
python實現(xiàn)自動發(fā)送郵件發(fā)送多人、群發(fā)、多附件的示例
下面小編就為大家分享一篇python實現(xiàn)自動發(fā)送郵件發(fā)送多人、群發(fā)、多附件的示例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-01-01python實現(xiàn)將文件夾下面的不是以py文件結尾的文件都過濾掉的方法
今天小編就為大家分享一篇python實現(xiàn)將文件夾下面的不是以py文件結尾的文件都過濾掉的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-10-10Python?matplotlib中plt.plot()函數(shù)的顏色參數(shù)設置及可以直接運行的程序代碼
在數(shù)據(jù)可視化中matplotlib.pyplot模塊的plot函數(shù)是一個非常重要且常用的工具,用于繪制2D圖形,這篇文章主要給大家介紹了關于Python?matplotlib中plt.plot()函數(shù)的顏色參數(shù)設置及可以直接運行的程序代碼,需要的朋友可以參考下2024-03-03Python實現(xiàn)線程狀態(tài)監(jiān)測簡單示例
這篇文章主要介紹了Python實現(xiàn)線程狀態(tài)監(jiān)測,結合簡單實例形式分析了Python線程start啟動、sleep推遲運行、isAlive判斷等方法使用技巧,需要的朋友可以參考下2018-03-03pytorch中的模型訓練(以CIFAR10數(shù)據(jù)集為例)
這篇文章主要介紹了pytorch中的模型訓練(以CIFAR10數(shù)據(jù)集為例),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06