如何通俗的理解面向?qū)ο缶幊?/h1>
點(diǎn)擊上方“小白學(xué)視覺(jué)”,選擇加"星標(biāo)"或“置頂”
重磅干貨,第一時(shí)間送達(dá)

本文轉(zhuǎn)自:磐創(chuàng)AI
面向?qū)ο缶幊袒騉OP對(duì)于初學(xué)者來(lái)說(shuō)可能是一個(gè)很難理解的概念。這主要是因?yàn)楹芏嗟胤蕉紱](méi)有正確的解釋。通常很多書籍都是從解釋OOP開始,討論三大術(shù)語(yǔ):封裝、繼承和多態(tài)性。但是當(dāng)這本書能夠解釋這些話題的時(shí)候,任何一個(gè)剛剛開始的人都會(huì)感到失落。
所以,我想讓程序員、數(shù)據(jù)科學(xué)家和蟒蛇愛(ài)好者們更容易理解這個(gè)概念。我打算這樣做的方法是去掉所有的行話,并通過(guò)一些例子。我將從解釋類和對(duì)象開始。然后我將解釋為什么類在各種情況下都很重要,以及它們是如何解決一些基本問(wèn)題的。這樣,讀者也能在帖子末尾理解這三大術(shù)語(yǔ)。
在這個(gè)名為Python Shorts的系列文章中,我將解釋Python提供的一些簡(jiǎn)單但非常有用的構(gòu)造、一些基本技巧以及我在數(shù)據(jù)科學(xué)工作中經(jīng)常遇到的一些用例。
這篇文章是關(guān)于解釋OOP的外行方式。
什么是對(duì)象和類 簡(jiǎn)單地說(shuō),Python中的一切都是對(duì)象,類是對(duì)象的藍(lán)圖。所以當(dāng)我們寫下:
a = 2
b = "Hello!"
我們正在創(chuàng)建一個(gè)int類的對(duì)象a,該對(duì)象的值為2,str類的對(duì)象b的值為“Hello!”. 當(dāng)我們?cè)谀J(rèn)情況下用兩個(gè)引號(hào)來(lái)提供字符串。
除此之外,我們中的許多人最終都會(huì)在沒(méi)有意識(shí)到的情況下使用類和對(duì)象。例如,當(dāng)你使用任何scikit-learn模型時(shí),實(shí)際上是在使用一個(gè)類。
clf = RandomForestClassifier()
clf.fit(X,y)
這里的分類器clf是一個(gè)對(duì)象,fit是一個(gè)在RandomForestClassifier類中定義的方法
為什么要使用類 因此,我們?cè)谑褂肞ython時(shí)經(jīng)常使用它們。但為什么呢。類是怎么回事?我可以用函數(shù)做同樣的事情嗎?
是的,你可以。但是與函數(shù)相比,類確實(shí)為你提供了很多功能。舉個(gè)例子,str類有很多為對(duì)象定義的函數(shù),我們只需按tab鍵就可以訪問(wèn)這些函數(shù)。人們也可以編寫所有這些函數(shù),但是那樣的話,只按tab鍵就不能使用它們了。

類的這個(gè)屬性稱為封裝。從Wikipedia來(lái)看,封裝是指將數(shù)據(jù)與操作該數(shù)據(jù)的方法捆綁在一起,或者限制對(duì)對(duì)象某些組件的直接訪問(wèn)。
所以這里str類綁定了數(shù)據(jù)(“Hello!)以及所有對(duì)我們的數(shù)據(jù)進(jìn)行操作的方法。我會(huì)在帖子的最后解釋聲明的第二部分。同樣,RandomForestClassifier 類將所有的方法(fit、predict等)捆綁在一起
除此之外,類的使用還可以幫助我們使代碼更加模塊化和易于維護(hù)。假設(shè)我們要?jiǎng)?chuàng)建一個(gè)像Scikit-Learn這樣的庫(kù)。我們需要?jiǎng)?chuàng)建許多模型,每個(gè)模型都有一個(gè)fit和predict方法。如果不使用類,我們將為每個(gè)不同的模型提供許多函數(shù),例如:
RFCFit
RFCPredict
SVCFit
SVCPredict
LRFit
LRPredict
and so on.
這種代碼結(jié)構(gòu)只是一場(chǎng)噩夢(mèng),因此Scikit Learn將每個(gè)模型定義為一個(gè)具有fit和predict方法的類。
創(chuàng)建類 所以,現(xiàn)在我們了解了為什么要使用類,它們是如何如此重要,我們?nèi)绾握嬲_始使用它們?所以,創(chuàng)建一個(gè)類非常簡(jiǎn)單。下面是你將要編寫的任何類的樣板代碼:
class myClass:
def __init__(self, a, b):
self.a = a
self.b = b
def somefunc(self, arg1, arg2):
# 這里有些代碼
我們?cè)谶@里看到很多新的關(guān)鍵字。主要是class、__init__和self。這些是什么?同樣,通過(guò)一些例子很容易解釋。
假設(shè)你在一家有很多賬戶的銀行工作。我們可以創(chuàng)建一個(gè)名為account的類,用于處理任何帳戶。例如,下面我創(chuàng)建了一個(gè)基本的玩具類帳戶,它為用戶存儲(chǔ)數(shù)據(jù),即帳戶名和余額。它還為我們提供了兩種銀行存款/取款的方法。一定要通讀一遍。它遵循與上面代碼相同的結(jié)構(gòu)。
class Account:
def __init__(self, account_name, balance=0):
self.account_name = account_name
self.balance = balance
def deposit(self, amount):
self.balance += amount
def withdraw(self,amount):
if amount <= self.balance:
self.balance -= amount
else:
print("Cannot Withdraw amounts as no funds!!!")
我們可以使用以下方法創(chuàng)建一個(gè)名為Rahul且金額為100的帳戶:
myAccount = Account("Rahul",100)
我們可以使用以下方法訪問(wèn)此帳戶的數(shù)據(jù):

但是,如何將這些屬性balance和account_name分別設(shè)置為100和“Rahul”?我們從來(lái)沒(méi)有調(diào)用過(guò)__init__方法,那么為什么對(duì)象會(huì)獲得這些屬性?這里的答案是,只要我們創(chuàng)建對(duì)象,它就會(huì)運(yùn)行。因此,當(dāng)我們創(chuàng)建myAccount時(shí),它還會(huì)自動(dòng)運(yùn)行函數(shù)__init__
所以現(xiàn)在我們明白了,讓我們?cè)囍嬉恍╁X到我們的賬戶里。我們可以通過(guò):

我們的余額上升到200英鎊。但是你有沒(méi)有注意到,我們的函數(shù)deposit需要兩個(gè)參數(shù),即self和amount,但是我們只提供了一個(gè)參數(shù),而且仍然有效。
那么,這個(gè)self是什么?有self的方法是用一種不同的方式調(diào)用同一個(gè)函數(shù)。下面,我調(diào)用屬于類account的同一個(gè)函數(shù)deposit,并向它提供myAccount對(duì)象和amount?,F(xiàn)在函數(shù)需要兩個(gè)參數(shù)。

我們的賬戶余額如預(yù)期增加了100。所以這是我們調(diào)用的同一個(gè)函數(shù)?,F(xiàn)在,只有self和myAccount是完全相同的對(duì)象時(shí),才會(huì)發(fā)生這種情況。我調(diào)用的時(shí)候我的賬戶存款(100)。Python為函數(shù)調(diào)用提供與參數(shù)self相同的對(duì)象myAccount。這就是為什么self.balance在函數(shù)定義中真正指的是myAccount.balance.
但是,仍然存在一些問(wèn)題

我們知道如何創(chuàng)建類,但是還有一個(gè)重要的問(wèn)題我還沒(méi)有提到。
所以,假設(shè)你正在與蘋果iPhone部門合作,并且必須為每種iPhone型號(hào)創(chuàng)建一個(gè)不同的類。對(duì)于這個(gè)簡(jiǎn)單的例子,讓我們假設(shè)我們的iPhone的第一個(gè)版本目前只做一件事——打電話并存儲(chǔ)。我們可以這樣寫:
class iPhone:
def __init__(self, memory, user_id):
self.memory = memory
self.mobile_id = user_id
def call(self, contactNum):
# 這里有些實(shí)現(xiàn)
現(xiàn)在,蘋果計(jì)劃推出iPhone1,這款iPhone機(jī)型引入了一項(xiàng)新功能——拍照功能。一種方法是復(fù)制粘貼上述代碼并創(chuàng)建一個(gè)新的類iPhone1,如下所示:
class iPhone1:
def __init__(self, memory, user_id):
self.memory = memory
self.mobile_id = user_id
self.pics = []
def call(self, contactNum):
# 這里有些實(shí)現(xiàn)
def click_pic(self):
# 這里有些實(shí)現(xiàn)
pic_taken = ...
self.pics.append(pic_taken)
但正如你所看到的,這是大量不必要的代碼重復(fù)(上面用粗體顯示),Python有一個(gè)消除代碼重復(fù)的解決方案。編寫iPhone1類的一個(gè)好方法是:
Class iPhone1(iPhone):
def __init__(self,memory,user_id):
super().__init__(memory,user_id)
self.pics = []
def click_pic(self):
# 這里有些實(shí)現(xiàn)
pic_taken = ...
self.pics.append(pic_taken)
這就是繼承的概念。根據(jù)Wikipedia的說(shuō)法:繼承是將一個(gè)對(duì)象或類基于另一個(gè)保留類似實(shí)現(xiàn)的對(duì)象或類的機(jī)制。簡(jiǎn)單地說(shuō),iPhone1現(xiàn)在可以訪問(wèn)類iPhone中定義的所有變量和方法。
在本例中,我們不必進(jìn)行任何代碼復(fù)制,因?yàn)槲覀円呀?jīng)從父類iPhone繼承(獲取)了所有方法。因此,我們不必再次定義調(diào)用函數(shù)。另外,我們不使用super在函數(shù)中設(shè)置mobile_uid和內(nèi)存。
**super().__init__(memory,user_id)**是什么?
在現(xiàn)實(shí)生活中,你的初始函數(shù)不是這些漂亮的兩行函數(shù)。你將需要在類中定義許多變量/屬性,并且復(fù)制并粘貼子類(這里是iphone1)會(huì)很麻煩。因此存在super().。這里super().__init__()實(shí)際上在這里調(diào)用父iPhone類的**__init__**方法。因此,在這里,當(dāng)類iPhone1的__init__函數(shù)運(yùn)行時(shí),它會(huì)自動(dòng)使用父類的__init__函數(shù)設(shè)置類的memory和user_id。
我們?cè)贛L/DS/DL中的哪里可以看到?下面是我們創(chuàng)建PyTorch模型。此模型繼承了nn.Module類,并使用super調(diào)用該類的__init__函數(shù)。
class myNeuralNet(nn.Module):
def __init__(self):
super().__init__()
# 在這里定義所有層
self.lin1 = nn.Linear(784, 30)
self.lin2 = nn.Linear(30, 10)
def forward(self, x):
# 在此處連接層輸出以定義前向傳播
x = self.lin1(x)
x = self.lin2(x)
return x
但什么是「多態(tài)」?我們?cè)絹?lái)越了解類是如何工作的,所以我想我現(xiàn)在就試著解釋多態(tài)??聪旅娴念?。
import math
class Shape:
def __init__(self, name):
self.name = name
def area(self):
pass
def getName(self):
return self.name
class Rectangle(Shape):
def __init__(self, name, length, breadth):
super().__init__(name)
self.length = length
self.breadth = breadth
def area(self):
return self.length*self.breadth
class Square(Rectangle):
def __init__(self, name, side):
super().__init__(name,side,side)
class Circle(Shape):
def __init__(self, name, radius):
super().__init__(name)
self.radius = radius
def area(self):
return pi*self.radius**2
這里我們有基類Shape和其他派生類-Rectangle和Circle。另外,看看我們?nèi)绾卧赟quare類中使用多個(gè)級(jí)別的繼承,Square類是從Rectangle派生的,而Rectangle又是從Shape派生的。每個(gè)類都有一個(gè)名為area的函數(shù),它是根據(jù)形狀定義的。
因此,通過(guò)Python中的多態(tài)性,一個(gè)同名函數(shù)可以執(zhí)行多個(gè)任務(wù)的概念成為可能。事實(shí)上,這就是多態(tài)性的字面意思:“具有多種形式的東西”。所以這里我們的函數(shù)area有多種形式。
多態(tài)性與Python一起工作的另一種方式是使用isinstance方法。因此,使用上面的類,如果我們這樣做:

因此,對(duì)象mySquare的實(shí)例類型是方形、矩形和形狀。因此對(duì)象是多態(tài)的。這有很多好的特性。例如,我們可以創(chuàng)建一個(gè)與Shape對(duì)象一起工作的函數(shù),它將通過(guò)使用多態(tài)性完全處理任何派生類(Square、Circle、Rectangle等)。

更多信息 為什么我們看到函數(shù)名或?qū)傩悦詥蜗聞澗€和雙下劃線開頭?有時(shí)我們想讓類中的屬性和函數(shù)私有化,而不允許用戶看到它們。這是封裝的一部分,我們希望“限制對(duì)對(duì)象某些組件的直接訪問(wèn)”。例如,假設(shè)我們不想讓用戶看到我們的iPhone創(chuàng)建后的memory(RAM)。在這種情況下,我們使用變量名中的下劃線創(chuàng)建屬性。
因此,當(dāng)我們以下面的方式創(chuàng)建iPhone類時(shí),你將無(wú)法訪問(wèn)你的memory或ipython私有函數(shù),因?yàn)樵搶傩袁F(xiàn)在使用_。

但你仍然可以使用(盡管不建議使用)更改變量值,

你還可以使用私有函數(shù)myphone._privatefunc()。如果要避免這種情況,可以在變量名前面使用雙下劃線。例如,在調(diào)用print(myphone.__memory)下面拋出一個(gè)錯(cuò)誤。此外,你無(wú)法使用myphone更改對(duì)象的內(nèi)部數(shù)據(jù)。myphone.__memory = 1。

但是,正如你所見(jiàn),你可以在類定義中的函數(shù)setMemory中訪問(wèn)和修改self.__memory
結(jié)論 
我希望這對(duì)你理解類很有用。仍然有很多類需要我在下一篇關(guān)于magic方法的文章中討論。敬請(qǐng)期待。另外,總結(jié)一下,在這篇文章中,我們學(xué)習(xí)了OOP和創(chuàng)建類以及OOP的各種基礎(chǔ)知識(shí):
封裝:對(duì)象包含自身的所有數(shù)據(jù)。
繼承:我們可以創(chuàng)建一個(gè)類層次結(jié)構(gòu),其中父類的方法傳遞給子類
多態(tài):函數(shù)有多種形式,或者對(duì)象可能有多種類型。
為了結(jié)束這篇文章,我會(huì)給你一個(gè)練習(xí),讓你去實(shí)現(xiàn),因?yàn)槲艺J(rèn)為這可能會(huì)為你澄清一些概念。創(chuàng)建一個(gè)類,使你可以使用體積和曲面面積管理三維對(duì)象(球體和立方體)?;緲影宕a如下所示:
import math
class Shape3d:
def __init__(self, name):
self.name = name
def surfaceArea(self):
pass
def volume(self):
pass
def getName(self):
return self.name
class Cuboid():
pass
class Cube():
pass
class Sphere():
pass
如果你想了解更多關(guān)于Python的知識(shí),我想從密歇根大學(xué)(universityofmichigan)調(diào)出一門關(guān)于學(xué)習(xí)中級(jí)Python的優(yōu)秀課程:https://bit.ly/shrea
下載1:OpenCV-Contrib擴(kuò)展模塊中文版教程
在「小白學(xué)視覺(jué)」公眾號(hào)后臺(tái)回復(fù):擴(kuò)展模塊中文教程,即可下載全網(wǎng)第一份OpenCV擴(kuò)展模塊教程中文版,涵蓋擴(kuò)展模塊安裝、SFM算法、立體視覺(jué)、目標(biāo)跟蹤、生物視覺(jué)、超分辨率處理等二十多章內(nèi)容。
下載2:Python視覺(jué)實(shí)戰(zhàn)項(xiàng)目52講 在「小白學(xué)視覺(jué)」公眾號(hào)后臺(tái)回復(fù):Python視覺(jué)實(shí)戰(zhàn)項(xiàng)目,即可下載包括圖像分割、口罩檢測(cè)、車道線檢測(cè)、車輛計(jì)數(shù)、添加眼線、車牌識(shí)別、字符識(shí)別、情緒檢測(cè)、文本內(nèi)容提取、面部識(shí)別等31個(gè)視覺(jué)實(shí)戰(zhàn)項(xiàng)目,助力快速學(xué)校計(jì)算機(jī)視覺(jué)。
下載3:OpenCV實(shí)戰(zhàn)項(xiàng)目20講 在「小白學(xué)視覺(jué)」公眾號(hào)后臺(tái)回復(fù):OpenCV實(shí)戰(zhàn)項(xiàng)目20講,即可下載含有20個(gè)基于OpenCV實(shí)現(xiàn)20個(gè)實(shí)戰(zhàn)項(xiàng)目,實(shí)現(xiàn)OpenCV學(xué)習(xí)進(jìn)階。
交流群
歡迎加入公眾號(hào)讀者群一起和同行交流,目前有SLAM、三維視覺(jué)、傳感器、自動(dòng)駕駛、計(jì)算攝影、檢測(cè)、分割、識(shí)別、醫(yī)學(xué)影像、GAN、算法競(jìng)賽等微信群(以后會(huì)逐漸細(xì)分),請(qǐng)掃描下面微信號(hào)加群,備注:”昵稱+學(xué)校/公司+研究方向“,例如:”張三 + 上海交大 + 視覺(jué)SLAM“。請(qǐng)按照格式備注,否則不予通過(guò)。添加成功后會(huì)根據(jù)研究方向邀請(qǐng)進(jìn)入相關(guān)微信群。請(qǐng)勿在群內(nèi)發(fā)送廣告,否則會(huì)請(qǐng)出群,謝謝理解~
瀏覽
38
點(diǎn)擊上方“小白學(xué)視覺(jué)”,選擇加"星標(biāo)"或“置頂”
重磅干貨,第一時(shí)間送達(dá)
本文轉(zhuǎn)自:磐創(chuàng)AI
面向?qū)ο缶幊袒騉OP對(duì)于初學(xué)者來(lái)說(shuō)可能是一個(gè)很難理解的概念。這主要是因?yàn)楹芏嗟胤蕉紱](méi)有正確的解釋。通常很多書籍都是從解釋OOP開始,討論三大術(shù)語(yǔ):封裝、繼承和多態(tài)性。但是當(dāng)這本書能夠解釋這些話題的時(shí)候,任何一個(gè)剛剛開始的人都會(huì)感到失落。
所以,我想讓程序員、數(shù)據(jù)科學(xué)家和蟒蛇愛(ài)好者們更容易理解這個(gè)概念。我打算這樣做的方法是去掉所有的行話,并通過(guò)一些例子。我將從解釋類和對(duì)象開始。然后我將解釋為什么類在各種情況下都很重要,以及它們是如何解決一些基本問(wèn)題的。這樣,讀者也能在帖子末尾理解這三大術(shù)語(yǔ)。
在這個(gè)名為Python Shorts的系列文章中,我將解釋Python提供的一些簡(jiǎn)單但非常有用的構(gòu)造、一些基本技巧以及我在數(shù)據(jù)科學(xué)工作中經(jīng)常遇到的一些用例。
這篇文章是關(guān)于解釋OOP的外行方式。
簡(jiǎn)單地說(shuō),Python中的一切都是對(duì)象,類是對(duì)象的藍(lán)圖。所以當(dāng)我們寫下:
a = 2
b = "Hello!"
我們正在創(chuàng)建一個(gè)int類的對(duì)象a,該對(duì)象的值為2,str類的對(duì)象b的值為“Hello!”. 當(dāng)我們?cè)谀J(rèn)情況下用兩個(gè)引號(hào)來(lái)提供字符串。
除此之外,我們中的許多人最終都會(huì)在沒(méi)有意識(shí)到的情況下使用類和對(duì)象。例如,當(dāng)你使用任何scikit-learn模型時(shí),實(shí)際上是在使用一個(gè)類。
clf = RandomForestClassifier()
clf.fit(X,y)
這里的分類器clf是一個(gè)對(duì)象,fit是一個(gè)在RandomForestClassifier類中定義的方法
因此,我們?cè)谑褂肞ython時(shí)經(jīng)常使用它們。但為什么呢。類是怎么回事?我可以用函數(shù)做同樣的事情嗎?
是的,你可以。但是與函數(shù)相比,類確實(shí)為你提供了很多功能。舉個(gè)例子,str類有很多為對(duì)象定義的函數(shù),我們只需按tab鍵就可以訪問(wèn)這些函數(shù)。人們也可以編寫所有這些函數(shù),但是那樣的話,只按tab鍵就不能使用它們了。

類的這個(gè)屬性稱為封裝。從Wikipedia來(lái)看,封裝是指將數(shù)據(jù)與操作該數(shù)據(jù)的方法捆綁在一起,或者限制對(duì)對(duì)象某些組件的直接訪問(wèn)。
所以這里str類綁定了數(shù)據(jù)(“Hello!)以及所有對(duì)我們的數(shù)據(jù)進(jìn)行操作的方法。我會(huì)在帖子的最后解釋聲明的第二部分。同樣,RandomForestClassifier 類將所有的方法(fit、predict等)捆綁在一起
除此之外,類的使用還可以幫助我們使代碼更加模塊化和易于維護(hù)。假設(shè)我們要?jiǎng)?chuàng)建一個(gè)像Scikit-Learn這樣的庫(kù)。我們需要?jiǎng)?chuàng)建許多模型,每個(gè)模型都有一個(gè)fit和predict方法。如果不使用類,我們將為每個(gè)不同的模型提供許多函數(shù),例如:
RFCFit
RFCPredict
SVCFit
SVCPredict
LRFit
LRPredict
and so on.
這種代碼結(jié)構(gòu)只是一場(chǎng)噩夢(mèng),因此Scikit Learn將每個(gè)模型定義為一個(gè)具有fit和predict方法的類。
所以,現(xiàn)在我們了解了為什么要使用類,它們是如何如此重要,我們?nèi)绾握嬲_始使用它們?所以,創(chuàng)建一個(gè)類非常簡(jiǎn)單。下面是你將要編寫的任何類的樣板代碼:
class myClass:
def __init__(self, a, b):
self.a = a
self.b = b
def somefunc(self, arg1, arg2):
# 這里有些代碼
我們?cè)谶@里看到很多新的關(guān)鍵字。主要是class、__init__和self。這些是什么?同樣,通過(guò)一些例子很容易解釋。
假設(shè)你在一家有很多賬戶的銀行工作。我們可以創(chuàng)建一個(gè)名為account的類,用于處理任何帳戶。例如,下面我創(chuàng)建了一個(gè)基本的玩具類帳戶,它為用戶存儲(chǔ)數(shù)據(jù),即帳戶名和余額。它還為我們提供了兩種銀行存款/取款的方法。一定要通讀一遍。它遵循與上面代碼相同的結(jié)構(gòu)。
class Account:
def __init__(self, account_name, balance=0):
self.account_name = account_name
self.balance = balance
def deposit(self, amount):
self.balance += amount
def withdraw(self,amount):
if amount <= self.balance:
self.balance -= amount
else:
print("Cannot Withdraw amounts as no funds!!!")
我們可以使用以下方法創(chuàng)建一個(gè)名為Rahul且金額為100的帳戶:
myAccount = Account("Rahul",100)
我們可以使用以下方法訪問(wèn)此帳戶的數(shù)據(jù):

但是,如何將這些屬性balance和account_name分別設(shè)置為100和“Rahul”?我們從來(lái)沒(méi)有調(diào)用過(guò)__init__方法,那么為什么對(duì)象會(huì)獲得這些屬性?這里的答案是,只要我們創(chuàng)建對(duì)象,它就會(huì)運(yùn)行。因此,當(dāng)我們創(chuàng)建myAccount時(shí),它還會(huì)自動(dòng)運(yùn)行函數(shù)__init__
所以現(xiàn)在我們明白了,讓我們?cè)囍嬉恍╁X到我們的賬戶里。我們可以通過(guò):

我們的余額上升到200英鎊。但是你有沒(méi)有注意到,我們的函數(shù)deposit需要兩個(gè)參數(shù),即self和amount,但是我們只提供了一個(gè)參數(shù),而且仍然有效。
那么,這個(gè)self是什么?有self的方法是用一種不同的方式調(diào)用同一個(gè)函數(shù)。下面,我調(diào)用屬于類account的同一個(gè)函數(shù)deposit,并向它提供myAccount對(duì)象和amount?,F(xiàn)在函數(shù)需要兩個(gè)參數(shù)。

我們的賬戶余額如預(yù)期增加了100。所以這是我們調(diào)用的同一個(gè)函數(shù)?,F(xiàn)在,只有self和myAccount是完全相同的對(duì)象時(shí),才會(huì)發(fā)生這種情況。我調(diào)用的時(shí)候我的賬戶存款(100)。Python為函數(shù)調(diào)用提供與參數(shù)self相同的對(duì)象myAccount。這就是為什么self.balance在函數(shù)定義中真正指的是myAccount.balance.
但是,仍然存在一些問(wèn)題

我們知道如何創(chuàng)建類,但是還有一個(gè)重要的問(wèn)題我還沒(méi)有提到。
所以,假設(shè)你正在與蘋果iPhone部門合作,并且必須為每種iPhone型號(hào)創(chuàng)建一個(gè)不同的類。對(duì)于這個(gè)簡(jiǎn)單的例子,讓我們假設(shè)我們的iPhone的第一個(gè)版本目前只做一件事——打電話并存儲(chǔ)。我們可以這樣寫:
class iPhone:
def __init__(self, memory, user_id):
self.memory = memory
self.mobile_id = user_id
def call(self, contactNum):
# 這里有些實(shí)現(xiàn)
現(xiàn)在,蘋果計(jì)劃推出iPhone1,這款iPhone機(jī)型引入了一項(xiàng)新功能——拍照功能。一種方法是復(fù)制粘貼上述代碼并創(chuàng)建一個(gè)新的類iPhone1,如下所示:
class iPhone1:
def __init__(self, memory, user_id):
self.memory = memory
self.mobile_id = user_id
self.pics = []
def call(self, contactNum):
# 這里有些實(shí)現(xiàn)
def click_pic(self):
# 這里有些實(shí)現(xiàn)
pic_taken = ...
self.pics.append(pic_taken)
但正如你所看到的,這是大量不必要的代碼重復(fù)(上面用粗體顯示),Python有一個(gè)消除代碼重復(fù)的解決方案。編寫iPhone1類的一個(gè)好方法是:
Class iPhone1(iPhone):
def __init__(self,memory,user_id):
super().__init__(memory,user_id)
self.pics = []
def click_pic(self):
# 這里有些實(shí)現(xiàn)
pic_taken = ...
self.pics.append(pic_taken)
這就是繼承的概念。根據(jù)Wikipedia的說(shuō)法:繼承是將一個(gè)對(duì)象或類基于另一個(gè)保留類似實(shí)現(xiàn)的對(duì)象或類的機(jī)制。簡(jiǎn)單地說(shuō),iPhone1現(xiàn)在可以訪問(wèn)類iPhone中定義的所有變量和方法。
在本例中,我們不必進(jìn)行任何代碼復(fù)制,因?yàn)槲覀円呀?jīng)從父類iPhone繼承(獲取)了所有方法。因此,我們不必再次定義調(diào)用函數(shù)。另外,我們不使用super在函數(shù)中設(shè)置mobile_uid和內(nèi)存。
**super().__init__(memory,user_id)**是什么?
在現(xiàn)實(shí)生活中,你的初始函數(shù)不是這些漂亮的兩行函數(shù)。你將需要在類中定義許多變量/屬性,并且復(fù)制并粘貼子類(這里是iphone1)會(huì)很麻煩。因此存在super().。這里super().__init__()實(shí)際上在這里調(diào)用父iPhone類的**__init__**方法。因此,在這里,當(dāng)類iPhone1的__init__函數(shù)運(yùn)行時(shí),它會(huì)自動(dòng)使用父類的__init__函數(shù)設(shè)置類的memory和user_id。
我們?cè)贛L/DS/DL中的哪里可以看到?下面是我們創(chuàng)建PyTorch模型。此模型繼承了nn.Module類,并使用super調(diào)用該類的__init__函數(shù)。
class myNeuralNet(nn.Module):
def __init__(self):
super().__init__()
# 在這里定義所有層
self.lin1 = nn.Linear(784, 30)
self.lin2 = nn.Linear(30, 10)
def forward(self, x):
# 在此處連接層輸出以定義前向傳播
x = self.lin1(x)
x = self.lin2(x)
return x
但什么是「多態(tài)」?我們?cè)絹?lái)越了解類是如何工作的,所以我想我現(xiàn)在就試著解釋多態(tài)??聪旅娴念?。
import math
class Shape:
def __init__(self, name):
self.name = name
def area(self):
pass
def getName(self):
return self.name
class Rectangle(Shape):
def __init__(self, name, length, breadth):
super().__init__(name)
self.length = length
self.breadth = breadth
def area(self):
return self.length*self.breadth
class Square(Rectangle):
def __init__(self, name, side):
super().__init__(name,side,side)
class Circle(Shape):
def __init__(self, name, radius):
super().__init__(name)
self.radius = radius
def area(self):
return pi*self.radius**2
這里我們有基類Shape和其他派生類-Rectangle和Circle。另外,看看我們?nèi)绾卧赟quare類中使用多個(gè)級(jí)別的繼承,Square類是從Rectangle派生的,而Rectangle又是從Shape派生的。每個(gè)類都有一個(gè)名為area的函數(shù),它是根據(jù)形狀定義的。
因此,通過(guò)Python中的多態(tài)性,一個(gè)同名函數(shù)可以執(zhí)行多個(gè)任務(wù)的概念成為可能。事實(shí)上,這就是多態(tài)性的字面意思:“具有多種形式的東西”。所以這里我們的函數(shù)area有多種形式。
多態(tài)性與Python一起工作的另一種方式是使用isinstance方法。因此,使用上面的類,如果我們這樣做:

因此,對(duì)象mySquare的實(shí)例類型是方形、矩形和形狀。因此對(duì)象是多態(tài)的。這有很多好的特性。例如,我們可以創(chuàng)建一個(gè)與Shape對(duì)象一起工作的函數(shù),它將通過(guò)使用多態(tài)性完全處理任何派生類(Square、Circle、Rectangle等)。

為什么我們看到函數(shù)名或?qū)傩悦詥蜗聞澗€和雙下劃線開頭?有時(shí)我們想讓類中的屬性和函數(shù)私有化,而不允許用戶看到它們。這是封裝的一部分,我們希望“限制對(duì)對(duì)象某些組件的直接訪問(wèn)”。例如,假設(shè)我們不想讓用戶看到我們的iPhone創(chuàng)建后的memory(RAM)。在這種情況下,我們使用變量名中的下劃線創(chuàng)建屬性。
因此,當(dāng)我們以下面的方式創(chuàng)建iPhone類時(shí),你將無(wú)法訪問(wèn)你的memory或ipython私有函數(shù),因?yàn)樵搶傩袁F(xiàn)在使用_。

但你仍然可以使用(盡管不建議使用)更改變量值,

你還可以使用私有函數(shù)myphone._privatefunc()。如果要避免這種情況,可以在變量名前面使用雙下劃線。例如,在調(diào)用print(myphone.__memory)下面拋出一個(gè)錯(cuò)誤。此外,你無(wú)法使用myphone更改對(duì)象的內(nèi)部數(shù)據(jù)。myphone.__memory = 1。

但是,正如你所見(jiàn),你可以在類定義中的函數(shù)setMemory中訪問(wèn)和修改self.__memory

我希望這對(duì)你理解類很有用。仍然有很多類需要我在下一篇關(guān)于magic方法的文章中討論。敬請(qǐng)期待。另外,總結(jié)一下,在這篇文章中,我們學(xué)習(xí)了OOP和創(chuàng)建類以及OOP的各種基礎(chǔ)知識(shí):
封裝:對(duì)象包含自身的所有數(shù)據(jù)。
繼承:我們可以創(chuàng)建一個(gè)類層次結(jié)構(gòu),其中父類的方法傳遞給子類
多態(tài):函數(shù)有多種形式,或者對(duì)象可能有多種類型。
為了結(jié)束這篇文章,我會(huì)給你一個(gè)練習(xí),讓你去實(shí)現(xiàn),因?yàn)槲艺J(rèn)為這可能會(huì)為你澄清一些概念。創(chuàng)建一個(gè)類,使你可以使用體積和曲面面積管理三維對(duì)象(球體和立方體)?;緲影宕a如下所示:
import math
class Shape3d:
def __init__(self, name):
self.name = name
def surfaceArea(self):
pass
def volume(self):
pass
def getName(self):
return self.name
class Cuboid():
pass
class Cube():
pass
class Sphere():
pass
如果你想了解更多關(guān)于Python的知識(shí),我想從密歇根大學(xué)(universityofmichigan)調(diào)出一門關(guān)于學(xué)習(xí)中級(jí)Python的優(yōu)秀課程:https://bit.ly/shrea
交流群
歡迎加入公眾號(hào)讀者群一起和同行交流,目前有SLAM、三維視覺(jué)、傳感器、自動(dòng)駕駛、計(jì)算攝影、檢測(cè)、分割、識(shí)別、醫(yī)學(xué)影像、GAN、算法競(jìng)賽等微信群(以后會(huì)逐漸細(xì)分),請(qǐng)掃描下面微信號(hào)加群,備注:”昵稱+學(xué)校/公司+研究方向“,例如:”張三 + 上海交大 + 視覺(jué)SLAM“。請(qǐng)按照格式備注,否則不予通過(guò)。添加成功后會(huì)根據(jù)研究方向邀請(qǐng)進(jìn)入相關(guān)微信群。請(qǐng)勿在群內(nèi)發(fā)送廣告,否則會(huì)請(qǐng)出群,謝謝理解~
