面向對象編程設計與開發
代碼優化和注重編程范式,兩種最重要的編程范式分別是面向過程編程和面向對象編程。
什么是面向對象
emmmm……大概就是把很多東西都變成對象,然后去調用它實現功能,不用去糾結實現的過程。每個人理解不同,-。-就這吧。
面向對象有什么
類
一個類即是對一類擁有相同屬性的對象的抽象化。在類中定義了這些對象共同具備的屬性、共同的方法。
類的格式:
class + 類名
類的加載順序
- 類內部一個縮進的所有代碼都是在py文件從上到下解釋的時候就已經被執行了。
- 包括類在內,代碼永遠都是從上到下依次執行。
類和對象的命名空間
類和對象存儲在兩塊命名空間內的:
- 只要是對一個對象名字直接賦值,那么就是在這個對象的空間內創建了新的屬性。
- 只要是對一個可變的數據類型內部的變化,那么仍然是所有的對象和類共享這個改變的成果。
- 所有的靜態變量都是用類名來操作,這樣修改就能被所有的對象感知到
- 如果是對於可變數據類型的靜態變量,操作的是這個數據內部的內容,也可以使用對象來調用。
類的組合使用
一個類的對象是另一個類對象的屬性,如:圓形類的對象,是圓環類對象的屬性,計算圓形相關數據的公式只和圓形類在一起,其余的用到公式的地方都是通過圓形類來使用的,公式與其他類之間的關系是一個“松耦合”的關系。
緊偶合就是模塊或者系統之間關系太緊密,存在相互調用。
松耦合系統通常是基於消息的系統,此時客戶端和遠程服務並不知道對方是如何實現的。
類的內置方法
__init__ : 構造函數,在生成對象時調用
__del__ : 析構函數,釋放對象時使用
__repr__ : 打印,轉換
__setitem__ : 按照索引賦值
__getitem__: 按照索引獲取值
__len__: 獲得長度
__cmp__: 比較運算
__call__: 函數調用
__add__: 加運算
__sub__: 減運算
__mul__: 乘運算
__truediv__: 除運算
__mod__: 求余運算
__pow__: 乘方
屬性
即字面意思,屬性,特性,特征。如所有人都是人這個類,有性別、年齡、身高這些共同具有的屬性。
實例化后的實例
經過詳細的屬性值描述而成的事物;也為具有這些特征的事物,經過類的實例化而編程的實例。
方法
即這類事物可以具備的功能。
面向對象的三大特性
封裝(Encapsulation)
封裝本質就是私有化,即限制外部得到,變為私有化的。
如何私有化:
python中用雙下划線開頭的方式將對象變為私有。
變為私有化的特點:
- 類中定義的__x只能在內部使用,如self.__x,引用的就是變形的結果。
- 這種變形其實正是針對外部的變形,在外部是無法通過__x這個名字訪問到的。
- 在子類定義的__x不會覆蓋在父類定義的__x,因為子類中變形成了:_子類名__x,而父類中變形成了:_父類名__x,即雙下滑線開頭的屬性在繼承給子類時,子類是無法覆蓋的。
注意:
1. 這種私有化並非真正的限制我們從外部直接訪問屬性,如果知道類名,也是可以__類名__屬性,然后就可以繼續調用了。
2. 在繼承沖,父類如果不想讓子類覆蓋自己的方法,可以將方法私有化。
class A: def fa(self): print('from A') def test(self): self.fa() class B(A): def fa(self): print('from B') b = B() b.test()
結果:
from B
將fa定義為私有之后的情況,即__fa:
class A: def __fa(self): print('from A') def test(self): self.__fa() class B(A): def __fa(self): print('from B') b = B() b.test()
結果:
from A
三個比較重要的裝飾器:
@property:
作用:把裝飾的一個方法偽裝成一個屬性,只讓看不讓改,調用時直接按屬性調用方法調用。
@classmethod:
作用:把一個方法從對象方法,變成一個類方法。常用在修改類中的變量時,防止因類名改變而導致出錯。
例如:修改類中的__discount值。
class Fruits: __discount = 0.8 def __init__(self, name, price): print('init', self) self.name = name self.__price = price @classmethod # 把一個方法從對象方法,變成一個類方法 def change_discount(cls, value): cls.__discount = value # cls代表當前類,當修改類名時不會對cls造成影響,代碼不會出錯 @classmethod def get_discount(cls): return cls.__discount print(Fruits.get_discount()) Fruits.change_discount(1) print(Fruits.get_discount())
@staticmethod:
聲明這個方法只是一個普通的不會使用任何和這個類中的變量相關的方法,即靜態方法。
封裝的好處:
- 數據封裝:將數據隱藏起來,然后對外提供操作數據的接口,並且可以在接口上附加對數據操作的限制,以此來完成對數據屬性操作的嚴格控制。
- 隔離復雜度,同時提升了安全性。
例如ATM:
class ATM: def __card(self): print('插卡') def __auth(self): print('用戶認證') def __input(self): print('輸入取款金額') def __print_bill(self): print('打印賬單') def __take_money(self): print('取款') def withdraw(self): self.__card() self.__auth() self.__input() self.__print_bill() self.__take_money() a = ATM() a.withdraw()
繼承(Inheritance)
概念:繼承指的是類與類之間的關系,分為單繼承和多繼承。繼承是一種創建新類的方式,新建的類稱為派生類或子類。
繼承分為經典類和新式類
- 只有在python2中才分新式類和經典類,python3中統一都是新式類。
- 在python2中,沒有顯式的繼承object類的類,以及該類的子類,都是經典類。
- 在python2中,顯式地聲明繼承object的類,以及該類的子類,都是新式類。
- 在python3中,無論是否繼承object,都默認繼承object,即python3中所有類均為新式類。
- python3中,如果沒有指定基類,python的類會默認繼承object類,object是所有python類的基類,它提供了一些常見方法(如__str__)的實現。
繼承與重用性、派生
開發過程中,我們已經定義了一個類A,然后又想新建立另外一個類B,但是類B的大部分內容與類A相同時,只有一部分不同,這時我們選擇繼承A中的東西,實現代碼重用,只有部分不同的地方進行派生。
代碼:
class A(): @staticmethod def fight(): print('原攻擊') @staticmethod def buy(): print('原購買') class B(A): @staticmethod def fight(): print('現攻擊') a = B() a.fight() a.buy()
結果:
現攻擊
原購買
繼承的實現
python會在mro列表上查找基類,直到找到第一個匹配這個屬性的類為止。這個mro列表的構造是通過一個C3線性化算法來實現的。它實際上就是合並所有父類的MRO列表並遵循如下三條准則:
- 子類會先於父類被檢查。
- 多個父類會根據它們在列表中的順序被檢查。
- 如果對下一個類存在兩個合法的選擇,選擇第一個父類。


class A(object): def test(self): print('from A') class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B): def test(self): print('from D') class E(C): def test(self): print('from E') class F(D,E): # def test(self): # print('from F') pass f1=F() f1.test() print(F.__mro__) #只有新式才有這個屬性可以查看線性列表,經典類沒有這個屬性 #新式類繼承順序:F->D->B->E->C->A #經典類繼承順序:F->D->B->A->E->C #python3中統一都是新式類 #pyhon2中才分新式類與經典類
抽象類
概念:抽象類是特殊的類,只能被繼承,不能被實例化。
抽象類的意義:抽象類中只能有抽象方法(沒有實現功能),該類不能被實例化,只能被繼承,且子類必須實現抽象方法。這一點與接口有點類似,但其實是不同的,看以下示例。
#一切皆文件 import abc #利用abc模塊實現抽象類 class All_file(metaclass=abc.ABCMeta): all_type='file' @abc.abstractmethod #定義抽象方法,無需實現功能 def read(self): '子類必須定義讀功能' pass @abc.abstractmethod #定義抽象方法,無需實現功能 def write(self): '子類必須定義寫功能' pass # class Txt(All_file): # pass # # t1=Txt() #報錯,子類沒有定義抽象方法 class Txt(All_file): #子類繼承抽象類,但是必須定義read和write方法 def read(self): print('文本數據的讀取方法') def write(self): print('文本數據的讀取方法') class Sata(All_file): #子類繼承抽象類,但是必須定義read和write方法 def read(self): print('硬盤數據的讀取方法') def write(self): print('硬盤數據的讀取方法') class Process(All_file): #子類繼承抽象類,但是必須定義read和write方法 def read(self): print('進程數據的讀取方法') def write(self): print('進程數據的讀取方法') wenbenwenjian=Txt() yingpanwenjian=Sata() jinchengwenjian=Process() #這樣大家都是被歸一化了,也就是一切皆文件的思想 wenbenwenjian.read() yingpanwenjian.write() jinchengwenjian.read() print(wenbenwenjian.all_type) print(yingpanwenjian.all_type) print(jinchengwenjian.all_type)
多態(Polymorphism)
在python中,處處是多態,一切皆對象,多態指的是一類事物有多種形態。
鴨子類型
python程序員通常根據這種行為來編寫程序。例如,如果想編寫現有對象的自定義版本,可以繼承該對象,也可以創建一個外觀和行為像,但與它無任何關系的全新對象,后者通常用於保存程序組件的松耦合度。
接口與歸一化設計
接口
概念
給使用者來調用自己功能的方式、方法或入口。
好處
- 歸一化讓使用者無需關心對象的類是什么,只需要的知道這些對象都具備某些功能就可以了,這極大地降低了使用者的使用難度。
- 歸一化使得高層的外部使用者可以不加區分的處理所有接口兼容的對象集合。
- 抽象類的本質還是類,指的是一組類的相似性,包括數據屬性(如all_type)和函數屬性(如read、write),而接口只強調函數屬性的相似性。
- 抽象類是一個介於類和接口直接的一個概念,同時具備類和接口的部分特性,可以用來實現歸一化設計
抽象類與接口(歸一化設計)
type和class、元類
type
type一個對象的時候,結果總是這個對象所屬的類
所有的類的類型都是type,python中任何class定義的類型其實都是type類型實例化的對象。
元類
對象是被創造出來的、被實例化出來的
類也是被創造出來的,特殊的方式來創造
常規創造的類,總有幾個特性:
- 能夠實例化
- 能有屬性
- 能有方法
元類、能夠幫助創造不同尋常的類
- 特殊的需求一:不能實例化
- 特殊的需求二:只能有一個實例
面向對象的軟件開發
- 面向對象分析(object oriented analysis ,OOA)
- 面向對象設計(object oriented design,OOD)
- 面向對象編程(object oriented programming,OOP)
- 面向對象測試(object oriented test,OOT)
- 面向對象維護(object oriendted soft maintenance,OOSM)
