本節開始,我們將討論面向對象編程的三大特征:封裝、繼承和多態。下面,我們將由簡至難,依次討論封裝、繼承、多態。
一. 封裝?enclosure
- 封裝:指隱藏類的實現細節,讓使用者不用關心這些細節;
- 目的:讓使用者通過盡可能少的方法(或屬性)操作對象;
- 如何封裝:通過私有屬性和方法;
- 私有屬性和方法:
- 以雙下劃線'__'開頭,不以雙下劃線結尾的標識符為私有成員;
- 私有成員只能用此類的方法進行訪問和修改
- 擴展:了解java的讀者可能知道,java中使用了private、default、protected、public關鍵字,實現了更豐富的封裝;
- 示例:
class A:
"""用私有屬性和私有方法封裝屬性和方法"""
__dd = 300
def __init__(self):
# 創建私有屬性,此屬性在類外無法訪問
self.__e = 100
@staticmethod
# 私有方法
def __en():
print('私有方法__en被調用!')
@classmethod
def get__dd(cls):
print("私有的類變量__dd:{}".format(cls.__dd))
def info(self):
print('A的實例方法info訪問私有屬性__e:', self.__e)
# 調用私有方法
self.__en()
if __name__ == "__main__":
a = A()
a.info()
a.__class__.get__dd()
# print(a.__e) #AttributeError: 'A' object has no attribute '__e'
# print(a.__en()) #AttributeError: 'A' object has no attribute '_en'
# 創建新變量(屬性)
a.__e = 'hello'
print(a.__e)
print(a.__dict__)
運行結果:
二.?繼承 inheritance 和 派生 derived
- 什么是繼承/派生:
- 繼承是指從已有的類中派生出新的類,新類具有原類的行為,并能擴展新的行為;
- 派生類就是從一個已有的類衍生出新的類,在新的類上可以添加新的屬性和行為;
- 作用:
- 用繼承派生機制,可以將一些共有功能加在基類中,實現代碼共享;(共有的屬性和方法向上提,形成抽象)
- 在不改變超類的代碼基礎上改變原有功能
- 名詞:
- 基類 base class / 超類 super class / 父類 father class
- 派生類 derived class / 子類
1. 單繼承
- 語法:
?? ??? ?class 類名(
基類名
):
?? ??? ??? ?語句塊
- 說明:單繼承是派生類由一個基類衍生而來的
- 類的__base__屬性:用來記錄此類的父類
- 子類對象可以當成父類對象來使用:
- 示例
class Human:
"""此類用來描述人類的共同行為"""
@staticmethod
def say(what):
print('說:', what)
@staticmethod
def walk(distance):
print('走了', distance, '公里')
class Student(Human):
"""描述學生的共同行為"""
@staticmethod
def study(subject):
print('學習', subject)
class Teacher(Student):
@staticmethod
def teach(content):
print('正在教', content)
if __name__ == "__main__":
h1 = Human()
h1.say('Today is a good day.')
h1.walk(5)
print("#########################")
s1 = Student()
s1.say('How are you?')
s1.walk(5)
s1.study('Python')
print("#########################")
t1 = Teacher()
t1.say('I am a teacher.')
t1.walk(3)
t1.teach('講解繼承派生')
t1.study('滑冰')
運行結果:
2. 覆蓋 override
- 概念:覆蓋是指在有繼承關系的類中,子類中實現了與父類同名的方法,子類實例調用該方法時,實際調用的是子類中覆蓋版本的方法,這種現象被稱為覆蓋;
- super 函數:
- super(type, obj):返回綁定超類的實例(要求obj必須為type類型的實例);
- super():返回綁定超類的實例,等同于super(__class__,實例方法的第一個參數self);
- super()必須放在方法內調用
- 作用:返回綁定超類的實例,用超類的實例來調用其父類的覆蓋方法;
- 顯示調用父類的構造方法:當子類中實現了__init__方法,父類的構造方法并不會被調用,此時需要顯示調用父類的構造方法:super().__init__(參數)
- 示例1:在方法內使用super()
class Human:
def __init__(self, n, a):
self.name = n
self.age = a
def info(self):
print('name:', self.name)
print('age:', self.age)
class Student(Human):
def __init__(self, n, a, s):
# 顯示調用父類的初始化方法
super().__init__(n, a)
self.score = s
def info(self):
"""# 覆蓋,子類只負責干子類的事情"""
super().info()
print('score:', self.score)
if __name__ == "__main__":
h1 = Human('Alex', 22)
h1.info()
s1 = Student('Thomas', 25, 99)
s1.info()
示例2:
class A:
def work(self):
print('A.work被調用!')
class B(A):
"""用super構造函數來間接調用父類的覆蓋版本的方法"""
def work(self):
print('B.work被調用!')
def super_work(self):
"""此方法先調用一下子類的方法,再調用一下父類的方法"""
self.work()
# super().work() #調用父類的work,不能在方法外調用
# super(B,self).work()
super(__class__, self).work()
if __name__ == "__main__":
a = A()
a.work()
print("###################")
b = B()
b.work()
# 方法外部,使用super()函數調用B的父類A的work方法
print("###################")
super(B, b).work()
print("###################")
b.super_work()
示例2運行結果:
2. 多繼承?multiple inheritance
- 概念:多繼承是指一個子類繼承自兩個或兩個以上的基類;
- 語法:class類名(基類名1, 基類名2...);
- 說明:
- 一個子類同時繼承自多個父類,父類中的方法可以同時被繼承下來;
- 如果兩個父類中有同名的方法,則在子類中又沒有覆蓋此方法時,調用結果難以確定
- 問題(缺陷):多繼承可能會有標識符(名字空間)沖突的問題;
- 多繼承的MRO(Method Resolution Order)問題:
- '類'的__mro__屬性:用來記錄屬性或方法的查找順序;
- 示例:
class A:
def m(self):
print('A.m()')
class B:
def m(self):
print('B.m()')
class C:
def m(self):
print('C.m()')
class D(A, B, C):
def m(self):
super(A, self).m()
super(B, self).m()
super().m()
print('D.m()')
if __name__ == "__mian__":
d = D()
d.m()
print(D.__mro__)
運行結果:
結果分析: super()函數根據MRO順序來查找方法
類D同時繼承自類A,B, C,且這些類中都有方法m,在調用super時,方法的查找順序是按照D.__mro__屬性指定的順序;在D.m()中首先調用的是super(A, self).m(),A的上一個是B,因此首先打印B.m();同理,super(B, self).m(),B的上一個是C,因此打印C.m()。
3. 用于類的函數:issubclass(cls, class_or_tuple)
- 判斷一個類是否繼承自其它的類,如果此類是 class 或 tuple 中的一個派生子類,則返回 True ,否則返回 False;
- 一切類型都是 object 的子類
三.?多態 polymorphic
- 什么是多態:多態是指在有繼承/派生關系的類中,調用“基類對象的方法”,實際能調用子類的覆蓋方法,這種現象被稱為多態;
- 說明:
- 多態“調用的方法與對象相關”,不與類型相關;
- Python全部對象都只有運行時狀態(動態),沒有'C++語言'里的編譯時狀態(靜態)
- 示例:
class Shape:
def draw(self):
print('Shape的draw()被調用')
class Point(Shape):
def draw(self):
print('正在畫一個點')
class Circle(Point):
def draw(self):
print('正在畫一個圓')
def my_draw(s):
"""示意多態的使用"""
# s.draw調用誰是在運行時由s的類動態決定
s.draw()
if __name__ == "__main__":
my_draw(Shape())
my_draw(Point())
my_draw(Circle())
運行結果:
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
