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ū)ο缶幊讨欣^承機(jī)制的重要組成部分,特別是在構(gòu)建 PyTorch 神經(jīng)網(wǎng)絡(luò)模型時尤為關(guān)鍵。
語法分解
讓我們逐部分解析這個表達(dá)式:
super()- 這是 Python 內(nèi)置函數(shù),用于返回一個代理對象,該對象將方法調(diào)用委托給父類或兄弟類。super(SingleConv, self)- 這部分創(chuàng)建了一個代理對象,指向SingleConv類的父類(在這個例子中是nn.Module)。第一個參數(shù)指定類本身,第二個參數(shù)通常是類的實(shí)例(即self)。super(SingleConv, self).__init__()- 通過代理對象調(diào)用父類的__init__()方法,確保父類的初始化代碼被執(zhí)行。
為什么這很重要?
一般繼承原則
在繼承關(guān)系中,子類需要確保父類被正確初始化,因?yàn)椋?/p>
- 父類可能設(shè)置了子類依賴的重要屬性和狀態(tài)
- 父類可能執(zhí)行了必要的初始化邏輯
- 如果不調(diào)用父類的初始化方法,繼承鏈就會斷開,子類將無法完全繼承父類的功能
在 PyTorch 中的特殊重要性
在 PyTorch 的 nn.Module 上下文中,調(diào)用 super().__init__() 尤為關(guān)鍵,因?yàn)椋?/p>
參數(shù)管理 -
nn.Module的初始化方法設(shè)置了追蹤和管理模型參數(shù)(如卷積層的權(quán)重和偏置)的機(jī)制模塊注冊 - 它建立了子模塊的注冊系統(tǒng),使 PyTorch 能夠識別模型的層次結(jié)構(gòu)
功能支持 - 它啟用了許多核心功能,包括:
- 參數(shù)遷移(使用
.to(device)將模型移動到 CPU/GPU) - 模型保存和加載(使用
torch.save()和torch.load()) - 訓(xùn)練和評估模式切換(
.train()和.eval()) - 自動求導(dǎo)支持
- 參數(shù)遷移(使用
如果省略會發(fā)生什么?
如果您省略 super().__init__() 調(diào)用,可能會導(dǎ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())) # 可能返回空列表,因?yàn)閰?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ù)時會自動使用當(dāng)前類和實(shí)例。
多重繼承中的作用
在涉及多重繼承的復(fù)雜情況下,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")
當(dāng)創(chuàng)建 D 的實(shí)例時,super() 確保每個父類的 __init__ 只被調(diào)用一次,遵循 Python 的 MRO 規(guī)則。
執(zhí)行結(jié)果
當(dāng)我們創(chuàng)建 D 類的實(shí)例(如 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)的機(jī)制來確定多重繼承中方法查找的順序。MRO 決定了當(dāng)調(diào)用 super() 時,Python 應(yīng)該按照什么順序查找父類的方法。
我們可以通過以下方式查看一個類的 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 的實(shí)例上查找方法時,會按照 D -> B -> C -> A -> object 的順序搜索。
super() 的工作原理
理解 super() 的關(guān)鍵在于:super() 不是簡單地調(diào)用父類的方法,而是調(diào)用 MRO 中當(dāng)前類之后的下一個類的方法。
當(dāng)在一個類中使用 super().__init__() 時,Python 會查找 MRO 中當(dāng)前類之后的下一個類,并調(diào)用其 __init__ 方法。這是一個非常強(qiáng)大的機(jī)制,尤其是在處理復(fù)雜的繼承結(jié)構(gòu)時。
詳細(xì)的執(zhí)行流程
讓我們逐步追蹤 D() 創(chuàng)建實(shí)例時的執(zhí)行流程:
調(diào)用 D.__init__()
- 執(zhí)行
super().__init__() - 根據(jù) MRO,
D之后的類是B,所以調(diào)用B.__init__()
- 執(zhí)行
進(jìn)入 B.__init__()
- 執(zhí)行
super().__init__() - 根據(jù) MRO,
B之后的類是C,所以調(diào)用C.__init__()
- 執(zhí)行
進(jìn)入 C.__init__()
- 執(zhí)行
super().__init__() - 根據(jù) MRO,
C之后的類是A,所以調(diào)用A.__init__()
- 執(zhí)行
進(jìn)入 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 的實(shí)例將輸出:
A init # 從 B.__init__ 調(diào)用 B init A init # 從 C.__init__ 調(diào)用,A 被初始化了兩次! C init D init
可以看到,A.__init__() 被調(diào)用了兩次!這可能導(dǎo)致資源重復(fù)分配、狀態(tài)不一致或其他問題。
總結(jié)
super(SingleConv, self).__init__() 這行代碼是確保 PyTorch 神經(jīng)網(wǎng)絡(luò)模塊正確初始化的關(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)文章
python3之讀取redis數(shù)據(jù)帶有‘b’的問題
這篇文章主要介紹了python3之讀取redis數(shù)據(jù)帶有‘b’的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09
python密碼學(xué)列置換密碼學(xué)習(xí)
這篇文章主要為大家介紹了python密碼學(xué)列置換密碼學(xué)習(xí)的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
python通過tcp發(fā)送xml報(bào)文的方法
今天小編就為大家分享一篇python通過tcp發(fā)送xml報(bào)文的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-12-12

