第 6 章 (2)面向對象進階

簡介

面向對象三大特徵介紹

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集

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章