自己以前整理的筆記,不太完整,后續(xù)會(huì)不斷更新。。。。
- [ ] __new__方法擴(kuò)展
- [ ] 魔法方法
- [ ] 什么情況下使用self,什么情況不用?
面向過(guò)程和面向?qū)ο笫莾煞N不同的編程方式
一、面向過(guò)程
過(guò)程:是早期的一種編程概念,類似于函數(shù),但只有執(zhí)行,沒有返回值
面向過(guò)程:把能實(shí)現(xiàn)某些獨(dú)立功能的代碼封裝成一個(gè)個(gè)函數(shù),然后順序調(diào)用不同的函數(shù)
特點(diǎn)
- 過(guò)程與步驟---怎么做?
- 若需求復(fù)雜,代碼也會(huì)變復(fù)雜
二、面向?qū)ο螅∣bject Oriented Programming)
具體的事物抽象化,抽象的事物具象化---前者針對(duì)實(shí)際存在的事物,后者針對(duì)抽象的事物
特點(diǎn)
- 相比于函數(shù),面向?qū)ο笫歉蟮姆庋b,一個(gè)對(duì)象可根據(jù)職責(zé)封裝多個(gè)方法
- 注重對(duì)象和職責(zé)---誰(shuí)來(lái)做?
- 適合復(fù)雜項(xiàng)目開發(fā),提供固定的套路
對(duì)象的三大特性
- 封裝:封裝屬性和方法
- 繼承:實(shí)現(xiàn)代碼的重用
- 多態(tài):對(duì)封裝和繼承的功能擴(kuò)展:對(duì)象調(diào)用的方法,在子類和父類中都可以有,父類中有,子類可重寫,由此不同的子類可實(shí)現(xiàn)不同的功能
三、類
- 抽象
- 屬性
- 方法
類用來(lái)創(chuàng)建對(duì)象
對(duì)象是類的具象、實(shí)例化
類只有一個(gè),對(duì)應(yīng)的對(duì)象有多個(gè):不同對(duì)象之間的屬性可能不同
三要素
- 類名:大駝峰命名
- 屬性:特征
- 方法:行為
01.對(duì)象
- 每一次實(shí)例化的對(duì)象所保存的地址都不同
- 臨時(shí)為對(duì)象添加屬性的方法:直接給對(duì)象賦值 -----不推薦,未修改類
dir()內(nèi)建函數(shù)
使用dir()可傳入任意對(duì)象,查看對(duì)象的所有屬性及方法
顯示出的__方法名__格式的方法是python提供的內(nèi)置屬性/方法
對(duì)象初始化-引用
- 創(chuàng)建對(duì)象后,變量保存的是對(duì)象在內(nèi)存中的地址
- 變量引用(指向)了新建的對(duì)象
- print打印變量,顯示該變量引用的對(duì)象是由哪一個(gè)類創(chuàng)建的對(duì)象,及內(nèi)存中的地址
對(duì)象創(chuàng)建過(guò)程-原理
- 當(dāng)使用 類名( ) 創(chuàng)建對(duì)象時(shí),會(huì)自動(dòng)執(zhí)行以下操作:
- 為對(duì)象在內(nèi)存中 分配空間 —— 創(chuàng)建對(duì)象
- 為對(duì)象的屬性 設(shè)置初始值 —— 初始化方法 _init_()
- 這個(gè) 初始化方法 就是
__init__方法,__init__是對(duì)象的內(nèi)置方法,創(chuàng)建對(duì)象時(shí)自動(dòng)調(diào)用該方法
__init__方法是 專門 用來(lái)定義一個(gè)類 具有哪些屬性的方法!
self
- 由 哪一個(gè)對(duì)象 調(diào)用的方法,方法內(nèi)的self就是 哪一個(gè)對(duì)象的引用
- 對(duì)象調(diào)用方法時(shí),不需要傳遞self參數(shù)
- 類中定義方法時(shí)可通過(guò)self.的方式訪問屬性及調(diào)用其他方法
02.內(nèi)置方法
_init_()
當(dāng)使用 類名( ) 創(chuàng)建對(duì)象時(shí),分配完內(nèi)存空間后,自動(dòng)調(diào)用該方法
- 創(chuàng)建對(duì)象時(shí)類名( )中的實(shí)參傳入到__init__( )中除self之外的其他形參中
- 若屬性有初始值則不適合作為形參在對(duì)象初始化的時(shí)候傳入,可在該方法內(nèi)直接賦值
- 若某屬性的初始值不確定,可在該方法內(nèi)賦值為None
_del_()與del
當(dāng)一個(gè)對(duì)象被從內(nèi)存銷毀前,會(huì)自動(dòng)調(diào)用該方法
class Cat:
def __init__(self, new_name):
self.name = new_name
print('初始化調(diào)用')
def __del__(self):
print('銷毀前調(diào)用')
# 整個(gè)程序結(jié)束后對(duì)象才會(huì)被銷毀
tom = Cat('Tom')
print(tom.name)
# del tom # 使用del,提前調(diào)用__del()銷毀對(duì)象
print('_'*50)
_str_()
定制化print出的內(nèi)容
- 必須要return 一個(gè)字符串
- 用于print()對(duì)象時(shí)的顯示結(jié)果
- 如果不定義該方法,打印對(duì)象時(shí)返回結(jié)果為十六進(jìn)制內(nèi)存地址
__class__屬性
每一個(gè)實(shí)例對(duì)象中都存在一個(gè)__class__屬性,指向創(chuàng)建該對(duì)象的類對(duì)象
03.私有屬性和私有方法
定義
私有屬性/私有方法:在屬性名稱/方法名稱前加__(兩個(gè)下劃線)
特點(diǎn)
私有屬性和方法無(wú)法在類外訪問,只能在類中訪問
偽私有:python中不存在真正意義上的私有,私有屬性和方法可以通過(guò)以下格式訪問
對(duì)象._類名.__屬性/方法
實(shí)際上是python對(duì)私有屬性和方法改名了
class Women:
def __init__(self, name):
self.name = name
# 不要問女生的年齡
self.__age = 18
def __secret(self):
print("我的年齡是 %d" % self.__age)
xiaofang = Women('小芳')
print(xiaofang._Women.__age)
xiaofang._Women.__secret()
注意:
# *-* coding:utf-8 *-*
# 私有屬性只能在類中定義
class Test(object):
def __init__(self, name):
self.__name = name
a = Test('老王')
print(a._Test__name) # 可訪問
print(a.__dict__) # 查看a對(duì)象的所有屬性
# 實(shí)際上是__name被解釋器名字重整為_Test__name
a.__name='老李' # 添加一個(gè)屬性,屬性名為__name
print(a.__name)
print(a.__dict__)
# 結(jié)果
老王
{'_Test__name': '老王'}
老李
{'_Test__name': '老王', '__name': '老李'}
四、繼承
概念:子類擁有父類的所有屬性和方法
子類在自己方法內(nèi)不能直接訪問父類的私有屬性和私有方法,但可以通過(guò)父類的公有方法間接訪問私有屬性和方法
屬性
- 繼承的傳遞性:‘孫類’不僅繼承父類的屬性和方法,還會(huì)繼承‘爺類’的屬性和方法
01. 多重繼承
子類方法的重寫:
父類的方法實(shí)現(xiàn)無(wú)法滿足子類的需求
- 覆蓋父類方法
- 對(duì)父類方法進(jìn)行擴(kuò)展:
- super( ).方法( ): super( )的主要作用是擴(kuò)展父類中已有方法的功能
若父類中都存在某方法,則一直向上找,直到找到為止
也就是說(shuō)向上只能找到第一個(gè)某方法
- super(class1,self).方法1():繼承class1父類的方法1
- 父類名.方法(self): 調(diào)用父類中的方法,若父類中沒有該方法則去父類的父類中查找
若所有父類中都存在同一名稱的方法,那么可以用這種方法調(diào)用特定類中的方法
super().__init__相對(duì)于 類名.__init__,在單繼承上用法基本無(wú)差,但在多繼承上有區(qū)別,super方法能保證每個(gè)父類的方法只會(huì)執(zhí)行一次,而使用類名的方法會(huì)導(dǎo)致方法被執(zhí)行多次,如下的案例:
http://www.cnblogs.com/dkblog/archive/2011/02/24/1980654.html
02. 多繼承
class B:
pass
class C:
pass
class A(B, C):
pass
開發(fā)時(shí)若多個(gè)父類中的屬性或方法名稱相同時(shí),應(yīng)盡量避免使用多繼承,否則容易混淆
python中針對(duì)類提供了內(nèi)置屬性__mro__可以查看子類對(duì)多繼承的父類的搜索順序
print(A.__mro__)
# 結(jié)果
# (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>)
- 在搜索方法時(shí),是按照
__mro__的輸出結(jié)果 從左至右 的順序查找的 - 如果在當(dāng)前類中 找到方法,就直接執(zhí)行,不再搜索
- 如果 沒有找到,就查找下一個(gè)類 中是否有對(duì)應(yīng)的方法,如果找到,就直接執(zhí)行,不再搜索
- 如果找到最后一個(gè)類,還沒有找到方法,程序報(bào)錯(cuò)
object類是python中所有類的基類,提供一些內(nèi)置的屬性和方法
新式類與舊式(經(jīng)典)類
- 新式類:
默認(rèn)以object類為基類
多繼承時(shí),搜索父類時(shí)采用廣度優(yōu)先原則
python3.x中都是新式類
- 經(jīng)典類:
不默認(rèn)以object類為基類
多繼承時(shí),搜索父類時(shí)采用深度優(yōu)先原則
python2.x中,若不指定父類,不會(huì)以object類作為基類
建議:為保證代碼在2.x和3.x中均能運(yùn)行,只要沒有父類,都寫上object作為父類
當(dāng)所有父類都有共同的父類時(shí),在到達(dá)共有父類之前先按深度優(yōu)先原則,再按廣度優(yōu)先原則進(jìn)行繼承
幾種多繼承方式:



一個(gè)坑
# 新式類繼承順序,廣度優(yōu)先原則
class A:
pass
class B(A):
pass
# 報(bào)錯(cuò):繼承順序回頭,C--->A--->B--->A
class C(A,B):
pass
print(C.__mro__)
# 正常:繼承順序,C--->B--->A--->?
class C(B,A):
pass
print(C.__mro__)
五、多態(tài)
不同的子類對(duì)象調(diào)用相同的父類方法,產(chǎn)生不同的執(zhí)行結(jié)果
特點(diǎn):
- 多態(tài)可增加代碼的靈活度
- 以繼承和重寫父類方法為前提
- 不影響父類的內(nèi)部
多態(tài)的好處就是,當(dāng)我們需要傳入Dog、Cat、Tortoise……時(shí),我們只需要接收Animal類型就可以了,因?yàn)镈og、Cat、Tortoise……都是Animal類型,然后,按照Animal類型進(jìn)行操作即可。由于Animal類型有run()方法,因此,傳入的任意類型,只要是Animal類或者子類,就會(huì)自動(dòng)調(diào)用實(shí)際類型的run()方法,這就是多態(tài)的意思:
對(duì)于一個(gè)變量,我們只需要知道它是Animal類型,無(wú)需確切地知道它的子類型,就可以放心地調(diào)用run()方法 ,而具體調(diào)用的run()方法是作用在Animal、Dog、Cat還是Tortoise對(duì)象上,由運(yùn)行時(shí)該對(duì)象的確切類型決定,這就是多態(tài)真正的威力:調(diào)用方只管調(diào)用,不管細(xì)節(jié),而當(dāng)我們新增一種Animal的子類時(shí),只要確保run()方法編寫正確,不用管原來(lái)的代碼是如何調(diào)用的。這就是著名的“開閉”原則:
**對(duì)擴(kuò)展開放:允許子類重寫父類方法函數(shù) **
**對(duì)修改封閉:不重寫,直接繼承父類方法函數(shù) **
六、類屬性和類方法
從類的實(shí)例化講起:
每次通過(guò) 類名( ) 創(chuàng)建對(duì)象的動(dòng)作有兩步:
在內(nèi)存中為對(duì)象 分配空間
調(diào)用初始化方法
__init__為 對(duì)象初始化
對(duì)象創(chuàng)建后,內(nèi)存 中就有了一個(gè)對(duì)象的 實(shí)實(shí)在在 的存在 —— 實(shí)例
對(duì)象的屬性叫實(shí)例屬性,對(duì)象調(diào)用的方法叫實(shí)例方法
01 內(nèi)存空間分配
如圖,一個(gè)類可實(shí)例化為多個(gè)對(duì)象,但
- 每一個(gè)對(duì)象 都有自己 獨(dú)立的內(nèi)存空間,保存各自不同的屬性
- 多個(gè)對(duì)象的方法,在內(nèi)存中只有一份,保存在類所在的內(nèi)存空間,在調(diào)用方法時(shí),需要把對(duì)象的引用 傳遞到方法內(nèi)部即可
- 在程序運(yùn)行時(shí),類會(huì)被加載到內(nèi)存
- 每個(gè)實(shí)例對(duì)象內(nèi)都存在一個(gè)內(nèi)置屬性__class__,保存著該屬性是由哪個(gè)類創(chuàng)建的


02 類是一個(gè)特殊的對(duì)象
class A: 定義的類屬于類對(duì)象
obj=A( ): 定義的對(duì)象屬于實(shí)例對(duì)象
- 類對(duì)象在內(nèi)存中只有一份
- 類對(duì)象也有自己的屬性和方法,分別叫類屬性 和類方法
- 通過(guò) 類名. 的方式可訪問類屬性和類方法
類對(duì)象與實(shí)例對(duì)象互相訪問權(quán)限
- 類對(duì)象-->實(shí)例對(duì)象
類對(duì)象不可以訪問實(shí)例對(duì)象的屬性和方法
- 實(shí)例對(duì)象-->類對(duì)象
實(shí)例對(duì)象可以訪問類屬性(前提:實(shí)例對(duì)象中無(wú)同名屬性)和類方法(且實(shí)例方法不能與類方法重名,否則無(wú)效,只調(diào)用類方法) ,
實(shí)例對(duì)象也可訪問靜態(tài)方法
03 類屬性
在類對(duì)象中定義的屬性,通常會(huì)用來(lái)記錄與該類有關(guān)的特征,而不會(huì)用于記錄具體對(duì)象的特征
應(yīng)用場(chǎng)景:通過(guò)類創(chuàng)建實(shí)例對(duì)象時(shí),如果每個(gè)對(duì)象需要具有相同名字的屬性,那么就使用類屬性,用一份既可
python中實(shí)例對(duì)象屬性的獲取存在一個(gè)向上查找的機(jī)制:
- 通過(guò)
對(duì)象名.屬性名方式調(diào)用屬性時(shí),首先在實(shí)例對(duì)象內(nèi)部查找 - 沒找到就會(huì)向上尋找同名的類屬性
所以,實(shí)例對(duì)象也可以通過(guò)對(duì)象名.類屬性的方式訪問類屬性(但不推薦)
注意:
如果給對(duì)象以對(duì)象名.類屬性=值的方式添加屬性,不會(huì)影響類屬性的值,只會(huì)給對(duì)象新建一個(gè)與類屬性同名的實(shí)例屬性
實(shí)例對(duì)象也可以通過(guò)self.__class__.類屬性給類屬性重新賦值
04 類方法
類方法 就是針對(duì) 類對(duì)象 定義的方法
在類方法內(nèi)部可以直接訪問類屬性或者調(diào)用其他的類方法
# 定義類方法
@classmethod
def 類方法名(cls, var1, var2):
pass
類方法形參第一個(gè)參數(shù)應(yīng)該是cls,其效果類似于實(shí)例方法的第一個(gè)形參self,哪一個(gè)類調(diào)用該方法,方法的cls就是那個(gè)類的引用
05 靜態(tài)方法
在類中既不需要訪問實(shí)例屬性和方法,也不需要訪問類屬性和方法的方法叫靜態(tài)方法
# 定義靜態(tài)方法
@staticmethod
def 靜態(tài)方法名():
pass
通過(guò)類名.調(diào)用靜態(tài)方法,實(shí)例對(duì)象也可以調(diào)用靜態(tài)方法
七、單例
單例設(shè)計(jì)模式
設(shè)計(jì)模式
- 是 前人工作的總結(jié)和提煉,通常,被人們廣泛流傳的設(shè)計(jì)模式都是針對(duì) 某一特定問題 的成熟的解決方案
- 使用 設(shè)計(jì)模式 是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性
單例設(shè)計(jì)模式
讓類創(chuàng)建的對(duì)象,在系統(tǒng)內(nèi)存中只有唯一的一個(gè)實(shí)例
即:每次執(zhí)行新建對(duì)象操作所返回對(duì)象的內(nèi)存地址是相同的
應(yīng)用:音樂播放器、打印機(jī).....
01.__new__(cls)
使用類創(chuàng)建對(duì)象時(shí),python解釋器先調(diào)用__new__( )給對(duì)象分配內(nèi)存空間,然后再調(diào)用__init__( )初始化
__new__( )是object基類提供的內(nèi)置靜態(tài)方法,其功能:
- 在內(nèi)存中為對(duì)象分配空間
- 返回對(duì)象的引用
python解釋器獲得其返回的對(duì)象引用后,將其作為參數(shù)傳遞給__init__( )方法的第一個(gè)參數(shù)self
由以上可見:
- 要實(shí)現(xiàn)單例設(shè)計(jì)模式,可通過(guò)重寫__new__( )方法來(lái)控制創(chuàng)建對(duì)象時(shí)內(nèi)存空間的分配
- 基類object
super().__new__(cls)可以幫助實(shí)現(xiàn)對(duì)象內(nèi)存的分配,并返回對(duì)象的引用,也就是說(shuō),此時(shí)super().__new__(cls)是一個(gè)對(duì)象的引用 - 重寫
__new__方法 一定要return super().__new__(cls),因?yàn)樗梢詭椭鷮?shí)現(xiàn)內(nèi)存空間分配的功能 - 調(diào)用時(shí)需要主動(dòng)傳遞cls參數(shù),傳遞的是類對(duì)象
- 由于要識(shí)別對(duì)象是否是初次調(diào)用,所以需要有一個(gè)類屬性來(lái)記錄實(shí)例對(duì)象的實(shí)例化次數(shù)
class MusicPlayer:
count = None
def __init__(self):
print('初始化播放器對(duì)象')
def __new__(cls):
if MusicPlayer.count is None:
MusicPlayer.count = super().__new__(cls) # 基類object中的__new__方法完成了內(nèi)存的分配并返回了對(duì)象的引用
print(MusicPlayer.count)
return MusicPlayer.count # 要返回對(duì)象的引用
player1 = MusicPlayer()
player2 = MusicPlayer()
print(player1)
print(player2)
02.__new__方法擴(kuò)展
兩個(gè)用法:用于重寫一些不可變類型數(shù)據(jù)的類如:str,int,tuple;元類?????