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__() 方法,確保父類被正確初始化。
這是面向?qū)ο缶幊讨欣^承機制的重要組成部分,特別是在構(gòu)建 PyTorch 神經(jīng)網(wǎng)絡模型時尤為關(guān)鍵。
語法分解
讓我們逐部分解析這個表達式:
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í)行。
為什么這很重要?
一般繼承原則
在繼承關(guān)系中,子類需要確保父類被正確初始化,因為:
- 父類可能設(shè)置了子類依賴的重要屬性和狀態(tài)
- 父類可能執(zhí)行了必要的初始化邏輯
- 如果不調(diào)用父類的初始化方法,繼承鏈就會斷開,子類將無法完全繼承父類的功能
在 PyTorch 中的特殊重要性
在 PyTorch 的 nn.Module 上下文中,調(diào)用 super().__init__() 尤為關(guān)鍵,因為:
參數(shù)管理 -
nn.Module的初始化方法設(shè)置了追蹤和管理模型參數(shù)(如卷積層的權(quán)重和偏置)的機制模塊注冊 - 它建立了子模塊的注冊系統(tǒng),使 PyTorch 能夠識別模型的層次結(jié)構(gòu)
功能支持 - 它啟用了許多核心功能,包括:
- 參數(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í)行結(jié)果
當我們創(chuàng)建 D 類的實例(如 d = D())時,輸出結(jié)果為:
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() 的關(guān)鍵在于:super() 不是簡單地調(diào)用父類的方法,而是調(diào)用 MRO 中當前類之后的下一個類的方法。
當在一個類中使用 super().__init__() 時,Python 會查找 MRO 中當前類之后的下一個類,并調(diào)用其 __init__ 方法。這是一個非常強大的機制,尤其是在處理復雜的繼承結(jié)構(gòu)時。
詳細的執(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í)行完畢
- 打印
圖解說明
下面是繼承結(jié)構(gòu)和執(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)不一致或其他問題。
總結(jié)
super(SingleConv, self).__init__() 這行代碼是確保 PyTorch 神經(jīng)網(wǎng)絡模塊正確初始化的關(guān)鍵步驟。它調(diào)用父類 nn.Module 的初始化方法,設(shè)置必要的內(nèi)部狀態(tài),并啟用 PyTorch 的核心功能。
在 Python 3 中,推薦使用更簡潔的 super().__init__() 語法。無論使用哪種形式,確保在每個繼承自 nn.Module 的類的 __init__ 方法中調(diào)用它,這是構(gòu)建正確功能的 PyTorch 模型的基礎(chǔ)。
簡單來說,這行代碼就像告訴您的類:“在我開始自己的初始化工作之前,請確保我從父類繼承的所有功能都已正確設(shè)置好。”
到此這篇關(guān)于Python中的super().__init__()用法詳解的文章就介紹到這了,更多相關(guān)Python中super().__init__()內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
pandas DataFrame數(shù)據(jù)轉(zhuǎn)為list的方法
下面小編就為大家分享一篇pandas DataFrame數(shù)據(jù)轉(zhuǎn)為list的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-04-04
python 通過SSHTunnelForwarder隧道連接redis的方法
今天小編就為大家分享一篇python 通過SSHTunnelForwarder隧道連接redis的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-02-02
Win10+python3.6+git運行出現(xiàn)問題的解決
這篇文章主要介紹了Win10+python3.6+git運行出現(xiàn)問題的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06
python中SSH遠程登錄設(shè)備的實現(xiàn)方法
本文主要介紹了python中SSH遠程登錄設(shè)備,python中支持SSH協(xié)議的模塊主要有Paramiko和netmiko兩種,本文主要介紹了netmiko模塊,具有一定的參考價值,感興趣的可以了解一下2022-04-04

