面向對象(一)
類是對一群具有相同 特征或者 行為的事物的一個統稱,是抽象的,不能直接使用
1)特征 被稱為 屬性(變量)
2)行為 被稱為 方法(函數)
案例改造 —— 給對象增加屬性
- 在
Python
中,要 給對象設置屬性,非常的容易,但是不推薦使用只需要在 類的外部的代碼 中直接通過.
設置一個屬性即可- 因為:對象屬性的封裝應該封裝在類的內部
註意:這種方式雖然簡單,但是不推薦使用!
tom.name = "Tom" ... lazy_cat.name = "大懶貓"
__del__
方法(知道)
-
在
Python
中- 當使用
類名()
創建對象時,為對象 分配完空間__init__
方法 - 當一個 對象被從內存中銷毀 前,會 自動 調用
__del__
方法
- 當使用
-
應用場景
__init__
改造初始化方法,可以讓創建對象更加靈活__del__
如果希望在對象被銷毀前,再做一些事情,可以考慮一下__del__
方法
-
生命周期
- 一個對象從調用
類名()
創建,生命周期開始 - 一個對象的
__del__
方法一旦被調用,生命周期結束 - 在對象的生命周期內,可以訪問對象屬性,或者讓對象調用方法
- 一個對象從調用
class Cat: def __init__(self, new_name): self.name = new_name print("%s 來了" % self.name) def __del__(self): print("%s 去了" % self.name) # tom 是一個全局變量 tom = Cat("Tom") print(tom.name) # del 關鍵字可以刪除一個對象 del tom print("-" * 50)
__str__
方法
- 在
Python
中,使用print
輸出 對象變量,默認情況下,會輸出這個變量 引用的對象 是 由哪一個類創建的對象,以及 在內存中的地址(十六進制表示) - 如果在開發中,希望使用
print
輸出 對象變量 時,能夠打印 自定義的內容,就可以利用__str__
這個內置方法了
註意:
__str__
方法必須返回一個字符串
class Cat: def __init__(self, new_name): self.name = new_name print("%s 來了" % self.name) def __del__(self): print("%s 去了" % self.name) def __str__(self): return "我是小貓:%s" % self.name tom = Cat("Tom") print(tom)
身份運算符
身份運算符用於 比較 兩個對象的 內存地址 是否一致 —— 是否是對同一個對象的引用
- 在
Python
中針對None
比較時,建議使用is
判斷
運算符 | 描述 | 實例 |
---|---|---|
is | is 是判斷兩個標識符是不是引用同一個對象 | x is y,類似 id(x) == id(y) |
is not | is not 是判斷兩個標識符是不是引用不同對象 | x is not y,類似 id(a) != id(b) |
is 與 == 區別:
is
用於判斷 兩個變量 引用對象是否為同一個 ==
用於判斷 引用變量的值 是否相等
>>> a = [1, 2, 3] >>> b = [1, 2, 3] >>> b is a False >>> b == a True
面向對象三大特性
- 封裝 根據 職責 將 屬性 和 方法 封裝 到一個抽象的 類 中
- 繼承 實現代碼的重用,相同的代碼不需要重復的編寫
- 多態 不同的對象調用相同的方法,產生不同的執行結果,增加代碼的靈活度(以 繼承 和 重寫父類方法 為前提)
專業術語
Dog
類是Animal
類的子類,Animal
類是Dog
類的父類,Dog
類從Animal
類繼承Dog
類是Animal
類的派生類,Animal
類是Dog
類的基類,Dog
類從Animal
類派生
對父類方法進行 擴展
- 如果在開發中,子類的方法實現 中 包含 父類的方法實現
- 父類原本封裝的方法實現 是 子類方法的一部分
- 就可以使用 擴展 的方式
- 在子類中 重寫 父類的方法
- 在需要的位置使用
super().父類方法
來調用父類方法的執行 - 代碼其他的位置針對子類的需求,編寫 子類特有的代碼實現
關於 super
- 在
Python
中super
是一個 特殊的類 super()
就是使用super
類創建出來的對象- 最常 使用的場景就是在 重寫父類方法時,調用 在父類中封裝的方法實現
父類的 私有屬性 和 私有方法
- 子類對象 不能 在自己的方法內部,直接 訪問 父類的 私有屬性 或 私有方法
- 子類對象 可以通過 父類 的 公有方法 間接 訪問到 私有屬性 或 私有方法
- 私有屬性、方法 是對象的隱私,不對外公開,外界 以及 子類 都不能直接訪問
- 私有屬性、方法 通常用於做一些內部的事情
多繼承的使用註意事項
問題的提出
- 如果 不同的父類 中存在 同名的方法,子類對象 在調用方法時,會調用 哪一個父類中的方法呢?
- (答:繼承的父類哪個在前面,就先調用它的方法)
提示:開發時,應該盡量避免這種容易產生混淆的情況! —— 如果 父類之間 存在 同名的屬性或者方法,應該 盡量避免 使用多繼承
Python 中的 MRO —— 方法搜索順序(知道)
Python
中針對 類 提供了一個 內置屬性__mro__
可以查看 方法 搜索順序- MRO 是
method resolution order
,主要用於 在多繼承時判斷 方法、屬性 的調用 路徑
print(C.__mro__)
輸出結果
(<class ‘__main__.C‘>, <class ‘__main__.A‘>, <class ‘__main__.B‘>, <class ‘object‘>)
- 在搜索方法時,是按照
__mro__
的輸出結果 從左至右 的順序查找的 - 如果在當前類中 找到方法,就直接執行,不再搜索
- 如果 沒有找到,就查找下一個類 中是否有對應的方法,如果找到,就直接執行,不再搜索
- 如果找到最後一個類,還沒有找到方法,程序報錯
新式類與舊式(經典)類
object
是Python
為所有對象提供的 基類,提供有一些內置的屬性和方法,可以使用dir
函數查看
- 新式類:以
object
為基類的類,推薦使用 -
經典類:不以
object
為基類的類,不推薦使用 -
在
Python 3.x
中定義類時,如果沒有指定父類,會 默認使用object
作為該類的 基類 ——Python 3.x
中定義的類都是 新式類 -
在
Python 2.x
中定義類時,如果沒有指定父類,則不會以object
作為 基類
新式類 和 經典類 在多繼承時 —— 會影響到方法的搜索順序
為了保證編寫的代碼能夠同時在 Python 2.x
和 Python 3.x
運行!
今後在定義類時,如果沒有父類,建議統一繼承自 object
class 類名(object): pass
創建類的實例之後,內存中會開辟一塊空間,保存的是這個實例對象還有實例屬性,實例方法卻是引用類裏面的實例方法。
要訪問類屬性有兩種方式:
- 類名.類屬性
- 對象.類屬性 (不推薦)(向上查找原則)
註意,如果使用 對象.類屬性 = 值
賦值語句,只會 給對象添加一個屬性,而不會影響到 類屬性的值
類方法
- 類屬性 就是針對 類對象 定義的屬性
- 使用 賦值語句 在
class
關鍵字下方可以定義 類屬性 - 類屬性 用於記錄 與這個類相關 的特征
- 使用 賦值語句 在
- 類方法 就是針對 類對象 定義的方法
- 在 類方法 內部可以直接訪問 類屬性 或者調用其他的 類方法
語法如下
@classmethod def 類方法名(cls): pass
- 類方法需要用 修飾器
@classmethod
來標識,告訴解釋器這是一個類方法 - 類方法的 第一個參數 應該是
cls
- 由 哪一個類 調用的方法,方法內的
cls
就是 哪一個類的引用 - 這個參數和 實例方法 的第一個參數是
self
類似 - 提示 使用其他名稱也可以,不過習慣使用
cls
- 由 哪一個類 調用的方法,方法內的
- 通過 類名. 調用 類方法,調用方法時,不需要傳遞
cls
參數 - 在方法內部
- 可以通過
cls.
訪問類的屬性 - 也可以通過
cls.
調用其他的類方法
- 可以通過
靜態方法
-
在開發時,如果需要在 類 中封裝一個方法,這個方法:
- 既 不需要 訪問 實例屬性 或者調用 實例方法
- 也 不需要 訪問 類屬性 或者調用 類方法
-
這個時候,可以把這個方法封裝成一個 靜態方法
語法如下
@staticmethod def 靜態方法名(): pass
通過 類名. 調用 靜態方法
提問
如果方法內部 即需要訪問 實例屬性,又需要訪問 類屬性,應該定義成什麽方法?
答案
- 應該定義 實例方法
- 因為,類只有一個,在 實例方法 內部可以使用 類名. 訪問類屬性
面向對象(一)