簡介
面向對象三大特徵介紹
Python 是面向對象的語言,也支持面向對象編程的三大特性:繼承、封裝(隱藏)、多態。
1、封裝(隱藏)
隱藏對象的屬性和實現細節,只對外提供必要的方法。相當於將“細節封裝起來”,只對外
暴露“相關調用方法”。
通過前面學習的“私有屬性、私有方法”的方式,實現“封裝”。Python 追求簡潔的 語法,
沒有嚴格的語法級別的“訪問控制符”,更多的是依靠程序員自覺實現。
2、繼承
繼承可以讓子類具有父類的特性,提高了代碼的重用性。
從設計上是一種增量進化,原有父類設計不變的情況下,可以增加新的功能,或者改進已有的算法。
3、多態
多態是指同一個方法調用,由於對象不同會產生不同的行爲。生活中這樣的例子比比
皆是:同樣是休息方法,人不同休息方法不同。張三休息是睡覺,李四休息是玩遊戲。
一、繼承
繼承是面向對象程序設計的重要特徵,也是實現“代碼複用”的重要手段。 如果一個新類繼承自
一個設計好的類,就直接具備了已有類的特徵,就大大降低了工作 難度。已有的類,我們稱爲
“父類或者基類”,新的類,我們稱爲“子類或者派生類”。
1、語法格式
Python 支持多重繼承,一個子類可以繼承多個父類。繼承的語法格式如下:
class 子類類名(父類 1[,父類 2,...]):
類體
如果在類定義中沒有指定父類,則默認父類是 object 類。也就是說,object 是所有類的父 類,
裏面定義了一些所有類共有的默認實現,比如:__new__()。
注意:
定義子類時,必須在其構造函數中調用父類的構造函數。調用格式如下:
父類名.__init__(self, 參數列表)
示例代碼:
class Person:
def __init__(self, name, age):
self.name = name
self.__age = age
def get_info(self):
print('學生的信息:{0}的年齡是{1}歲'.format(self.name, self.__age))
class Student(Person):
def __init__(self, name, age, score):
self.score = score
# 構造函數中包含調用父類構造函數。(根據需要,不是必須)
# 子類並不會自動調用父類的__init__(),我們必須顯式的調用它。
Person.__init__(self, name, age)
s1 = Student('Jack', 14, 88)
s1.get_info()
print('s1對象內容:'dir(s1))
===================運行結果=========================
學生的信息:Jack的年齡是14歲
s1對象內容:['_Person__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', '__weakref__', 'get_info', 'name', 'score']
===================運行結果=========================
# 我們可以看到子類‘Student’中擁有父類‘Person’的實例屬性、方法等。
2、類成員的繼承和重寫
1. 成員繼承:子類繼承了父類除構造方法之外的所有成員。
2. 方法重寫:子類可以重新定義父類中的方法,這樣就會覆蓋父類的方法,也稱爲“重寫”
示例代碼:
class Person:
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
def get_info(self):
print('獲得信息1:{0}的年齡是{1}歲!'.format(self.name, self.age))
def country_info(self):
print('國籍信息1:{0}來自{1}'.format(self.name, self.address))
class Student(Person):
def __init__(self, name, age, address, score):
self.score = score
Person.__init__(self, name, age, address) # 構造函數中包含調用父類構造函數
def class_info(self):
print('{0}是大一XX班的學生'.format(self.name))
def get_info(self): # 重寫父類的方法
print('獲得信息2:{0}學生的成績是{1}分!'.format(self.name, self.score))
def country_info(self): # 重寫父類的方法
print('國籍信息2:{0}是個留學生,來自{1}'.format(self.name, self.address))
s1 = Student('Jack', 20, '英國', 77)
s1.class_info()
s1.get_info()
s1.country_info()
# 查看類的繼承層次結構:通過類的方法mro()或者類的屬性__mro__可以輸出這個類的繼承層次結構。
print('Student類的繼承層次結構:', Student.mro())
print('s1對象屬性:', dir(s1)) # dir()查看對象屬性
===================運行結果=========================
Jack是大一XX班的學生
獲得信息2:Jack學生的成績是77分!
國籍信息2:Jack是個留學生,來自英國
Student類的繼承層次結構: [<class '__main__.Student'>, <class '__main__.Person'>, <class 'object'>]
s1對象屬性:['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', '__weakref__', 'address', 'age', 'class_info', 'country_info',
'get_info', 'name', 'score']
===================運行結果=========================
# 注:object類是所有類的父類,因此所有的類都有object類的屬性和方法。
3、重寫__str__()方法
object 有一個__str__()方法,用於返回一個對於“對象的描述”,對應於內置函數 str() 經常用於
print()方法,幫助我們查看對象的信息。__str__()可以重寫。
示例代碼:
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
# 將對象轉化成一個字符串,一般用於 print 方法
return '{0}學生的年齡是{1}歲!'.format(self.name, self.age)
s1 = Student('Leo', 14)
print(s1)
===================運行結果=========================
Leo學生的年齡是14歲!
===================運行結果=========================
二、多重繼承
Python 支持多重繼承,一個子類可以有多個“直接父類”。這樣,就具備了“多個父 類”的特點。
但是由於,這樣會被“類的整體層次”搞的異常複雜,儘量避免使用。
示例代碼:
# 多重繼承
class Animal:
def __init__(self, name):
self.name = name
def run(self):
print('{0}跑的快!'.format(self.name))
def eat(self):
print('{0}吃東西特別快!'.format(self.name))
class People:
def __init__(self, name):
self.name = name
def learn(self):
print('{0}學習算術!'.format(self.name))
def eat(self):
print('{0}吃東西比較慢!'.format(self.name))
class Dog1(Animal, People):
def __init__(self, name):
Animal.__init__(self,name)
People.__init__(self,name)
# def eat(self):
# print('{0}喜歡抓兔子吃!'.format(self.name))
class Dog2(People, Animal):
def __init__(self, name):
Animal.__init__(self,name)
People.__init__(self,name)
# def eat(self):
# print('{0}喜歡抓兔子吃!'.format(self.name))
dog1 = Dog1('Dog1')
dog1.run()
dog1.learn()
dog1.eat()
print('--------------華麗的分割線-----------------')
dog2 = Dog2('Dog2')
dog2.run()
dog2.learn()
dog2.eat()
# MRO()
# Python 支持多繼承,如果父類中有相同名字的方法,在子類沒有指定父類名時,解釋器將
# “從左向右”按順序搜索。 MRO(Method Resolution Order):方法解析順序。 我們可以
# 通過 mro()方法獲得 “類的層次結構”,方法解析順序也是按照這個“類的層次結構”尋找的。
# 如果子類中也有重名的方法,首先會執行子類中的方法(此時的方法是方法的重寫)。
print('Dog1 MRO:', Dog1.mro())
print('Dog2 MRO:', Dog2.mro())
===================運行結果=========================
Dog1跑的快!
Dog1學習算術!
Dog1吃東西特別快!
--------------華麗的分割線-----------------
Dog2跑的快!
Dog2學習算術!
Dog2吃東西比較慢!
Dog1 MRO: [<class '__main__.Dog1'>, <class '__main__.Animal'>, <class '__main__.People'>, <class 'object'>]
Dog2 MRO: [<class '__main__.Dog2'>, <class '__main__.People'>, <class '__main__.Animal'>, <class 'object'>]
===================運行結果=========================
1、super()獲得父類定義
super()代表父類的定義,不是父類對象。
示例代碼:
# super 測試
class Animal:
def __init__(self, name):
self.name = name
def run(self):
print('{0}跑的快!'.format(self.name))
class Dog(Animal):
def __init__(self, name):
Animal.__init__(self, name)
def run(self):
super().run() # 通過super調用父類的方法run()
dog = Dog('二哈')
dog.run()
===================運行結果=========================
二哈跑的快!
===================運行結果=========================
三、多態
多態(polymorphism)是指同一個方法調用由於對象不同可能會產生不同的行爲。
關於多態要注意以下 2 點:
1. 多態是方法的多態,屬性沒有多態。
2. 多態的存在有 2 個必要條件:繼承、方法重寫。
示例代碼:
# 多態測試
class Animal:
def shout(self):
print('動物在黑夜中一直叫!')
class Dog(Animal):
def shout(self):
print('小狗汪汪叫!')
class Cat(Animal):
def shout(self):
print('小貓喵喵叫!')
def animal_shout(animal):
if isinstance(animal, Animal): # 傳入的對象不同,shout方法對應的實際行爲也不同。
animal.shout()
dog = Dog()
cat = Cat()
animal_shout(dog)
animal_shout(cat)
===================運行結果=========================
小狗汪汪叫!
小貓喵喵叫!
===================運行結果=========================
四、特殊方法和運算符重載
常見的特殊方法統計如下:
每個運算符實際上都對應了相應的方法,統計如下:
五、特殊屬性
Python 對象中包含了很多雙下劃線開始和結束的屬性,這些是特殊屬性,有特殊用法。
這裏我們列出常見的特殊屬性:
六、對象的淺拷貝和深拷貝
1、變量的賦值操作
只是形成兩個變量,實際還是指向同一個對象。
2、淺拷貝
Python拷貝一般都是淺拷貝。拷貝時,對象包含的子對象內容不拷貝。因此,源對象和拷貝對象會引用同一個子對象。
3、深拷貝
使用 copy 模塊的deepcopy函數,遞歸拷貝對象中包含的子對象。源對象和拷貝對象 所有的子對象也不同。
七、組合
“is-a”關係,我們可以使用“繼承”。從而實現子類擁有的父類的方法和屬性。“is-a” 關係指的是
類似這樣的關係:狗是動物,dog is animal。狗類就應該繼承動物類。
“has-a”關係,我們可以使用“組合”,也能實現一個類擁有另一個類的方法和屬性。” has-a”關
係指的是這樣的關係:手機擁有 CPU。 MobilePhone has a CPU。
示例代碼:
# 測試組合
class MobilePhone:
def __init__(self, cpu, screen):
self.cpu = cpu
self.screen = screen
class CPU:
def calculate(self):
print('CPU具有運算功能。')
class Screen:
def show(self):
print('Screen可以顯示一個好看的畫面。')
cpu = CPU()
screen = Screen()
phone = MobilePhone(cpu, screen)
phone.cpu.calculate()
phone.screen.show()
===================運行結果=========================
CPU具有運算功能。
Screen可以顯示一個好看的畫面。
===================運行結果=========================
八、設計模式_工廠模式實現
設計模式是面嚮對象語言特有的內容,是我們在面臨某一類問題時候固定的做法,設計模式有很多種,
比較流行的是:GOF(Goup Of Four)23 種設計模式。
對於初學者,我們學習兩個最常用的模式:工廠模式和單例模式。
工廠模式實現了創建者和調用者的分離,使用專門的工廠類將選擇實現類、創建對象進行統一的管理
和控制。
示例代碼:
# 工廠模式
class Carfactory:
def create_car(self, brand):
if brand == 'BMW':
return BMW()
elif brand == 'BYD':
return BYD()
elif brand == 'BenC':
return BenC()
else:
print('未知品牌的汽車!!!')
class BMW:
def run(self):
print('BMW車跑的快!')
class BYD:
def run(self):
print('BYD車跑的慢!')
class BenC:
def run(self):
print('BenC車跑的比較快!')
factory = Carfactory()
car1 = factory.create_car('BMW')
car1.run()
car2 = factory.create_car('BYD')
car2.run()
car3 = factory.create_car('QQ')
print('car1(BMW) obj:{0}'.format(car1))
print('car2(BYD) obj:{0}'.format(car2))
===================運行結果=========================
BMW車跑的快!
BYD車跑的慢!
未知品牌的汽車!!!
car1(BMW) obj:<__main__.BMW object at 0x0000022F7CA2FB38>
car2(BYD) obj:<__main__.BYD object at 0x0000022F7CA2FC88>
===================運行結果=========================
九、設計模式_單例模式實現
單例模式(Singleton Pattern)的核心作用是確保一個類只有一個實例,並且提供一 個訪問該實例
的全局訪問點。
單例模式只生成一個實例對象,減少了對系統資源的開銷。當一個對象的產生需要比較多的資源,
如讀取配置文件、產生其他依賴對象時,可以產生一個“單例對象”,然後永久駐留內存中,從而極大
的降低開銷。
單例模式有多種實現的方式,我們這裏推薦重寫__new__()的方法。
示例代碼:
# 測試單例模式
class MySingleton:
__obj = None
__init_flag = True
def __new__(cls, *args, **kwargs):
if cls.__obj == None:
cls.__obj = object.__new__(cls)
return cls.__obj
def __init__(self, name):
if MySingleton.__init_flag:
print('init......')
self.name = name
MySingleton.__init_flag = False
a = MySingleton('aa')
print('a:', a)
b = MySingleton('bb')
print('b:', b)
===================運行結果=========================
init......
a: <__main__.MySingleton object at 0x000001D20F662C50>
b: <__main__.MySingleton object at 0x000001D20F662C50>
===================運行結果=========================
# 此時我們會發現a、b同屬於一個對象,而且初始化只執行了一次。
上述聯繫的汽車工廠類(CarFactory)實現單利模式的示例代碼:
# 工廠類(CarFactory)實現單利模式
class CarFactory:
__obj = None
__init_flag = True
def __new__(cls, *args, **kwargs):
if cls.__obj == None:
cls.__obj = object.__new__(cls)
return cls.__obj
def __init__(self):
if CarFactory.__init_flag:
print('CarFactory init ......')
CarFactory.__init_flag = False
def creat_car(self, brand):
if brand == 'BMW':
return BMW()
elif brand == 'BYD':
return BYD()
elif brand == 'BenC':
return BenC()
else:
print('未知品牌的汽車!!!')
class BMW:
def run(self):
print('BMW車跑的快!')
class BYD:
def run(self):
print('BYD車跑的慢!')
class BenC:
def run(self):
print('BenC車跑的比較快!')
factory1 = CarFactory()
car1 = factory1.creat_car('BMW')
car1.run()
car2 = factory1.creat_car('BYD')
car2.run()
factory2 = CarFactory()
car3 = factory2.creat_car('BenC')
car3.run()
car4 = factory2.creat_car('QQ')
===================運行結果=========================
CarFactory init ......
BMW車跑的快!
BYD車跑的慢!
BenC車跑的比較快!
未知品牌的汽車!!!
===================運行結果=========================
學習來自:北京尚學堂高琪老師 Python 400集