OOP三大特性,且三個特性是有順序的:
- 封裝
- 繼承
- 多型
封裝
指的就是把現實世界的事務,封裝、抽象成程式設計裡的物件,包括各種屬性和方法。
這個一般都很簡單,不需要多講。
唯一要注意的就是:推薦從小往大開始封裝、開發類。比如
手槍,子彈
這兩個類,我們需要先定義和開發子彈的所有屬性和方法,然後再去開發上一層的手槍。這樣的話會很方便。反過來開發手槍的適合,發現寫到一半寫不下去要到子彈那裡寫,就很亂了。
繼承
子類可以繼承父類和父父類的所有屬性、方法。
繼承格式:
class Parent:
def func1(self):
pass
class Son(Parent):
def func2(self):
func1()
方法改寫:
子類在不滿意時,也可以進行自己的改寫父類的屬性、方法。
其中有兩種情況:
- Overwrite 覆蓋重寫父類方法:只需要寫一個同名函式即可覆蓋。
-
Extend 擴充套件父類函式:
- 第一種方式(主要):寫一個同名函式,並在其中通過
super().func()
引用父類方法。其中super
是一個python builtin 特殊類,而super()
即生成一個super的例項。在子類中生成super例項,會得到父類的引用。 - 第二種方式(python 2.x以前使用):寫一個同名函式,再通過
ParentName.func(self)
引用父類方法。但是不推薦,因為父類名稱改變的話所有的子類都要改。
- 第一種方式(主要):寫一個同名函式,並在其中通過
私有不繼承:
子類能夠繼承的只是父類的公開內容,但是不包括父類的私有內容。
如果要訪問的話也可以,但是需要間接的呼叫父類再用方法呼叫私有內容。
多繼承
Python中,子類是可以同時有多個父類的:也就是能夠同時繼承多個父類的所有屬性、方法。
繼承格式:
class Father:
def func1(self):
pass
class Mother:
def func2(self):
pass
class Son(Father, Mother):
def func3(self):
func1()
func2()
注意:
如果多個父類間存在有同名的方法,那麼會繼承第一個父類的方法。
MRO, Method Resolution Order
檢視繼承順序:
通過類自帶的.__mro__
屬性(MRO, Method Resolution Order
),可以檢視這個類的繼承順序。
子類可以直接寫FatherName.func()
來呼叫父級函式。
但是當子類用super().func()
時候,python就會根據MRO
順序,由近到遠逐次尋找,找到最近的上級則返回。
用上例,如果是多繼承的話,那麼尋找順序是:SON -> Father -> Mother -> object
。
檢視類的內建屬性和方法:
dir(className)
可以檢視內建所有屬性方法。
Python內建的object基礎類
Python3開始使用新式的類定義,即預設讓所有定義的類都自動繼承一個叫object
的內建基礎類。object
基礎類定義了很多方便的屬性。包括18項之多。
而舊式的Python2.x時代,不繼承object基礎類,自己定義的類就只有__doc__
和__module__
兩樣內建屬性而已。2.x時代,如果需要手動繼承,如:
class MyClass(object):
pass
多型
多型是指,不同的子類物件呼叫相同的父類方法,會產生多型多樣結果的程式設計特性。
多型的前提是能夠繼承父類的方法,且能夠重寫改寫父類的方法。
多型的特點:
- 是呼叫方法的技巧,而不影響類的內部設計
- 可以增加程式碼靈活度
def Father():
def work(self):
do_job()
def do_job(self):
print(`Farming on the field...`)
def Son(Father):
def do_job(self):
print(`Programming at an office...`)
# ---- Now let`s work ----
Jason = Son()
Jason.work()
以上程式碼中,同樣是work()
函式,且要do_work()
。但是,不同的人呼叫的是不同的do_work
。
Father呼叫自己的do_work,兒子因為自己重寫了do_work,所以呼叫自己的方法。
這就是多型——所繼承的方法,不需要再特殊指定誰用什麼方法,而物件會自動呼叫適合自己的方法。
類與例項
Python中,例項是一個物件,類也是一個物件,一切皆物件。但這也是Python OOP中引起很多麻煩的原因。
例項物件非常好理解,也好用,直接用,就不說了。但是類物件
就不那麼好理解了。
簡單說,類物件
也是一個標準的物件,有自己的屬性和方法,只不過能夠像模版
一樣生成多個例項物件而已。
類物件有這兩大研究點:
-
類屬性:就是能讓所有例項訪問和操作的公用廁所
- 定義類屬性:位於class的所有方法之外
- 訪問類屬性:className.propertyName
-
類方法:比較難理解,必須用到名為
@classmethod
的裝飾器,函式的第一個引數必須是關鍵字cls
,如同self
。-
@classmethod
裝飾器:用來告訴直譯器這是一個類方法
,而不是例項方法。 -
cls
引數:
-
類屬性與例項屬性
這是Python OOP中困擾很多人的特點。但是其實不難理解,總結如下:
class MyClass:
# 在這個位置定義的,叫類屬性。==等同於其它語言的“靜態屬性”
# 這是每個例項共有的公用屬性,相當於宿舍的公用洗澡間
count = 0
def __init__(self):
# 用self.定義的,叫例項屬性,是每個例項只自己所有的屬性,selfish
self.name = "Jason"
訪問類屬性的方法有兩種:
-
ClassName.propertyName
:推薦,直接用類名訪問類屬性。 -
Instance.propertyName
:不推薦用例項名訪問類屬性,因為如果需要寫入操作,那麼這種方法只會給自己新增一個例項屬性,而不會影響類屬性。
動態新增類屬性
方法一:
>>> MyClass.newAttribute = `I am a class attribute`
>>> print( MyClass.newAttribute )
`I am a class attribute`
方法二:裝飾器
# Customized decorator for classproperty
class classproperty(object):
def __init__(self, getter):
self.getter= getter
def __get__(self, instance, owner):
return self.getter(owner)
class MyClass:
@classproperty
def newAttribute(cls):
return `I am a class attribute.`
>>> print( MyClass.newAttribute )
`I am a class attribute`
之所以把方法封裝為一個類屬性,是因為我們有時候需要根據其它類屬性來定製這個類屬性。
而一般情況下,我們沒法在類屬性定義的時候獲得當前的類或類中其它的屬性。
類方法
類方法如同類屬性,是屬於全類的方法,但是(推薦)只用來訪問類屬性。
類方法:比較難理解,必須用到名為@classmethod
的裝飾器,函式的第一個引數必須是關鍵字cls
,如同self
。
-
@classmethod
裝飾器:用來告訴直譯器這是一個類方法
,而不是例項方法。 -
cls
引數:如同self
,用來指代當前的類。
注意:@classmethod
和cls
都是關鍵字,不能改。
程式碼:
class MyClass:
# 定義一個“類屬性”
count = 0
# 這裡開始定義“類方法”
@classmethod
def func(cls):
print(cls.count)
類靜態方法
類中的staticmethod
裝飾器同樣是python基礎類object的一個用於包裝、裝飾的方法。一旦在類方法前放上裝飾器@staticmethod
,方法就會轉換為一個靜態方法
。
靜態方法就是一個非常獨立的方法:既不訪問例項的資訊,也不訪問類的資訊。
程式碼:
class MyClass:
# 定義一個“類屬性”
count = 0
# 這裡開始定義“類方法”
@staticmethod
def func():
pass
Property屬性
類中的property
裝飾器,也是python基礎類object的一個用於包裝、裝飾的方法。一旦類方法前放上裝飾器@property
,方法就會轉換為一個類屬性
。很多時候把方法偽裝成屬性,是非常方便的。
class MyClass:
# 這裡開始定義由方法轉換為“類屬性”
@property
def name(self):
return "Jason"
c = MyClass()
print( c.name )
在繼承object基礎類的情況下,python給出了三種類屬性裝飾,對應三種操作:
- 讀取:
@property
- 寫入:
@name.setter
- 刪除:
@name.deleter
也就是說,當你讀取類屬性my_name
的時候,會呼叫被@property
修飾的方法;當你修改my_name
當時候,會呼叫被@my_name.setter
修飾的方法;當你刪除這個屬性時,會呼叫被@my_name.deleter
修飾的方法。
注意:
- 其中
@property
,@*.setter
,@*.deleter
,這是固定的名字,不能改。 - 三種操作所修飾的三個函式,必須都是同一個名字:即“類屬性”名。
程式碼:
class MyClass:
# 這裡開始定義由方法轉換為“類屬性”
@property
def name(self):
return "Jason"
@name.setter
def name(self, value):
self.name = value
@name.deleter
def name(self):
del "Jason"
c = MyClass()
print( c.name ) # READ
c.name = "Brown" # SET
del c.name # DELETE
property屬性的應用
很多OOP語言,針對property屬性
,一般操作是:一個私有屬性,配合兩個公有方法。
如:
class MyClass:
def __init__(self):
self.__name = "Jason"
def get_name(self):
return self.__name
def set_name(self, value):
self.__name = value
c = MyClass()
# 開始呼叫
c.set_name("Brownee")
print( c.get_name() )
在Python下,可以利用裝飾器改為以下程式碼,極大方便呼叫的過程:
class MyClass:
def __init__(self):
self.__name = "Jason"
@property
def name(self):
return self.__name
@name.setter
def name(self, value):
self.__name = value
c = MyClass()
# 開始呼叫
c.name = "Brownee"
print( c.name )