【python學習筆記】Python面向物件的理解(封裝,繼承,多型)
說明
提到面向物件,總是離不開幾個重要的術語:多型(Polymorphism),繼承(Inheritance)和封裝(Encapsulation)。Python也是一種支援OOP的動態語言,本文將簡單闡述Python對面向物件的支援。
在討論Python的OOP之前,先看幾個OOP術語的定義:
- 類:對具有相同資料和方法的一組物件的描述或定義。
- 物件:物件是一個類的例項。
- 例項(instance):一個物件的例項化實現。
- 標識(identity):每個物件的例項都需要一個可以唯一標識這個例項的標記。
- 例項屬性(instance attribute):一個物件就是一組屬性的集合。
- 例項方法(instance method):所有存取或者更新物件某個例項一條或者多條屬性的函式的集合。
- 類屬性(classattribute):屬於一個類中所有物件的屬性,不會只在某個例項上發生變化
- 類方法(classmethod):那些無須特定的對性例項就能夠工作的從屬於類的函式。
封裝
封裝,顧名思義就是將內容封裝到某個地方,以後再去呼叫被封裝在某處的內容。
對於面向物件的封裝來說,其實就是使用構造方法將內容封裝到 物件 中,然後通過物件直接或者self間接獲取被封裝的內容。
class Foo:
def __init__(self, name, age ,gender):
self.name = name
self.age = age
self.gender = gender
def eat(self):
print "%s,%s歲,%s,吃奶" %(self.name, self.age, self.gender)
def he(self):
print "%s,%s歲,%s,喝水" %(self.name, self.age, self.gender)
def shui(self):
print "%s,%s歲,%s,睡覺" %(self.name, self.age, self.gender)
a = Foo('jack', 10, '男')
a.eat()
a.he()
a.shui()
b = Foo('rose' , 11, '女')
b.eat()
b.he()
b.shui()
繼承
繼承,面向物件中的繼承和現實生活中的繼承相同,即:子可以繼承父的內容。
例如:
貓可以:喵喵叫、吃、喝、拉、撒
狗可以:汪汪叫、吃、喝、拉、撒
公共的部分就是 吃、喝、拉、撒
如下實現:
class Animal:
def eat(self):
print "%s 吃 " %self.name
def drink(self):
print "%s 喝 " %self.name
def shit(self):
print "%s 拉 " %self.name
def pee(self):
print "%s 撒 " %self.name
class Cat(Animal):
def __init__(self, name):
self.name = name
self.breed = '貓'
def cry(self):
print '喵喵叫'
class Dog(Animal):
def __init__(self, name):
self.name = name
self.breed = '狗'
def cry(self):
print '汪汪叫'
# ######### 執行 #########
c1 = Cat('貓one')
c1.eat()
c2 = Cat('貓two')
c2.drink()
d1 = Dog('狗one')
d1.eat()
注意: 關於多繼承
- 在Python中,如果父類和子類都重新定義了構造方法init( ),在進行子類例項化的時候,子類的構造方法不會自動呼叫父類的構造方法,必須在子類中顯示呼叫。
- Python的類可以繼承多個類,Java和C#中則只能繼承一個類
- Python的類如果繼承了多個類,那麼其尋找方法的方式有兩種,分別是:深度優先和廣度優先
- 當類是經典類時,多繼承情況下,會按照深度優先方式查詢,當類是新式類時,多繼承情況下,會按照廣度優先方式查詢
經典類和新式類,從字面上可以看出一個老一個新,新的必然包含了跟多的功能,也是之後推薦的寫法,從寫法上區分的話,如果 當前類或者父類繼承了object類,那麼該類便是新式類,否則便是經典類。
多型
首先Python不支援多型,也不用支援多型,python是一種多型語言,崇尚鴨子型別。
在程式設計中,鴨子型別(英語:duck typing)是動態型別的一種風格。在這種風格中,一個物件有效的語義,不是由繼承自特定的類或實現特定的介面,而是由當前方法和屬性的集合決定。這個概念的名字來源於由James Whitcomb Riley提出的鴨子測試,“鴨子測試”可以這樣表述:
“當看到一隻鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子。”
在鴨子型別中,關注的不是物件的型別本身,而是它是如何使用的。例如,在不使用鴨子型別的語言中,我們可以編寫一個函式,它接受一個型別為鴨的物件,並呼叫它的走和叫方法。在使用鴨子型別的語言中,這樣的一個函式可以接受一個任意型別的物件,並呼叫它的走和叫方法。如果這些需要被呼叫的方法不存在,那麼將引發一個執行時錯誤。任何擁有這樣的正確的走和叫方法的物件都可被函式接受的這種行為引出了以上表述,這種決定型別的方式因此得名。
鴨子型別通常得益於不測試方法和函式中引數的型別,而是依賴文件、清晰的程式碼和測試來確保正確使用。從靜態型別語言轉向動態型別語言的使用者通常試圖新增一些靜態的(在執行之前的)型別檢查,從而影響了鴨子型別的益處和可伸縮性,並約束了語言的動態特性。
例子:
class A:
def prt(self):
print "A"
class B(A):
def prt(self):
print "B"
class C(A):
def prt(self):
print "C"
class D(A):
pass
class E:
def prt(self):
print "E"
class F:
pass
def test(arg):
arg.prt()
a = A()
b = B()
c = C()
d = D()
e = E()
f = F()
test(a)
test(b)
test(c)
test(d)
test(e)
test(f)
# 結果
A
B
C
A
E
Traceback (most recent call last):
File "/Users/shikefu678/Documents/Aptana Studio 3 Workspace/demo/demo.py", line 33, in <module>
test(a),test(b),test(c),test(d),test(e),test(f)
File "/Users/shikefu678/Documents/Aptana Studio 3 Workspace/demo/demo.py", line 24, in test
arg.prt()
AttributeError: F instance has no attribute 'prt'
沒有誰規定test方法是接收的引數是什麼型別的。test方法只規定,接收一個引數,呼叫這個引數的prt方法。在執行的時候如果這個引數有prt方法,python就執行,如果沒有,python就報錯,因為abcde都有prt方法,而f沒有,所以得到了上邊得結果,這就是python的執行方式。