python類之杏彩平臺制作特殊屬性和魔術(shù)方法

一 python特殊屬性杏彩平臺制作?Q> 1279829431【源碼鏈接】dashengba.com

1 總述

屬性含義

_name_類,函數(shù),方法等的名字

_module_類定義所現(xiàn)在的模塊名

_class_對象或類所屬的類

_bases_類的基類的元素谨娜,順序為他們在基類列表中出現(xiàn)的順序

_doc_類/函數(shù)的文檔字符傳眠菇,如果沒有定義則為None

_mro_類的mro,class.mro()返回

_dict_類或?qū)嵗膶傩裕蓪懙淖值?/p>

_dir_返回了類或者對象所有成員列表业扒,dir()函數(shù)調(diào)用的是_dir_(),如果提供了_dir_()舒萎,則返回屬性列表程储,否則盡可能從__dict__屬性中收集信息

2 name

獲取類和函數(shù)的名字

#!/usr/local/bin/python3.6#coding:utf-8classA:passclassB(A):passdefC():passprint(A.__name__,B.__name__,C.__name__,sep='\n')

3 module

類定義所在的模塊名

#!/usr/local/bin/python3.6#coding:utf-8classA:passclassB:passprint(A.__module__,B.__module__)

#!/usr/local/bin/python3.6#coding:utf-8importdispprint(disp.A.__module__,disp.B.__module__,sep='\n')

4 class

對象或類所屬的類

#!/usr/local/bin/python3.6#coding:utf-8classA:passclassB(A):passa=A()b=B()print(A.__class__,B.__class__,sep='\n')#類所屬的類是classprint(a.__class__,b.__class__,sep='\n')# 對象所屬的類是實實在在的類

5 bases

類的基類的元組,順序是他們在基類列表中出現(xiàn)的順序

#!/usr/local/bin/python3.6#coding:utf-8classA:passclassB(A):passclassC(B):passclassE:passclassD(E,C):passprint(A.__bases__,B.__bases__,C.__bases__,D.__bases__,sep='\n')

結(jié)果如下

6 DOC

文檔字符串臂寝,針對類和函數(shù)有效章鲤,若不存在,則返回為None

#!/usr/local/bin/python3.6#coding:utf-8classA:'''this? is? class'''passdefB():'''this is? function'''passclassC:passprint(A.__doc__,B.__doc__,C.__doc__,sep='\n')

結(jié)果如下?

7 mro

類的mro咆贬。返回多繼承中的查找順序

#!/usr/local/bin/python3.6#coding:utf-8classA:passclassB(A):passclassC(B):passclassE:passclassD(E,C):passprint(A.__mro__,B.__mro__,C.__mro__,D.__mro__,sep='\n')

結(jié)果如下

8 dict

類或者實例的屬性败徊,可寫的字典

#!/usr/local/bin/python3.6#coding:utf-8classA:a=10def__init__(self,x):self.x=5a=A(3)print(A.__dict__)print(a.__dict__)

結(jié)果如下?

9 dir

dir 返回了類或者對象所有成員名稱列表,dir()函數(shù)調(diào)用的是_dir_()素征,如果提供了_dir_() ,則返回屬性的列表集嵌,否則會盡量從__dict__屬性中收集

dir() 對于不同類型的對象具有不同的行為:

1 如果對象是模塊對象,則列表包含模塊的屬性名

#!/usr/local/bin/python3.6#coding:utf-8importredeffoo(x):y=1print(dir())# 輸出當前模塊信息,此處會打印當前導(dǎo)入的模塊和導(dǎo)入的函數(shù)print(dir(re))print('+'*20)print(dir(foo))

結(jié)果如下

2 如果對象是類型或者類對象御毅,列表包含類的屬性名根欧,以及其他基類的屬性名

#!/usr/local/bin/python3.6#coding:utf-8classA:a='1234'def__init__(self):passclassB(A):# 此處調(diào)用父類,其dir中會包含父類的屬性passprint(dir())# 輸出當前模塊信息,此處會打印當前導(dǎo)入的模塊和導(dǎo)入的函數(shù)print('*'*40)print(dir(A),dir(B),sep='\n')# 此中DIR屬性父類和子類是完全相同的端蛆,但dict中卻是不同的print(A.__dict__,B.__dict__,sep='\n')

3 如果是對象凤粗,列表包含對象的屬性名,它的類的屬性名和基類的屬性名

#!/usr/local/bin/python3.6#coding:utf-8classA:a='1234'def__init__(self):self.x=10classB(A):# 此處調(diào)用父類,其dir中會包含父類的屬性passa=A()print(dir())# 輸出當前模塊信息,此處會打印當前導(dǎo)入的模塊和導(dǎo)入的函數(shù)print('*'*40)print(dir(A),dir(B),dir(a),sep='\n')#此處若是打印實例的屬性嫌拣,則會吧類的屬性也打印上來

結(jié)果如下?

4此處對屬性名進行了重寫操作

#!/usr/local/bin/python3.6#coding:utf-8classA:a='1234'def__init__(self):self.x=10classB(A):# 此處調(diào)用父類柔袁,其dir中會包含父類的屬性def__dir__(self):# 此處是針對實例設(shè)置的,和類本身并無關(guān)系return['this is class A ']# 此處是dir返回是列表异逐,若使用字符串捶索,則會處理成列表進行返回a=A()b=B()print(dir())# 輸出當前模塊信息,此處會打印當前導(dǎo)入的模塊和導(dǎo)入的函數(shù),以及實例后的對象print('*'*40)print(dir(A),dir(B),dir(a),dir(b),sep='\n')#此處若是打印實例的屬性灰瞻,則會吧類的屬性也打印上來

結(jié)果如下?

二 python 實例屬性之魔術(shù)方法

1 分類

描述方法

初始化和銷毀_init__和_del\

在字典和set中使用_hash_

布爾類型腥例,常用于判斷語句_bool_

可視化,用于輸出對應(yīng)的類型_str__和_repr\

運算符重載_eq_,_ne_,_gt_,__lt__等

容器和大小相關(guān)和操作相關(guān)屬性_getitem_,__setitem__等

可調(diào)用對象,將實例化的對象當成一個函數(shù)去調(diào)用酝润,一旦可以當函數(shù)調(diào)用_call_

上下文管理(with open(x) as f 等形式_enter_,_exit_

反射_getattr_, _setattr_,_delattr_

描述器Object._get_(self,instance,owner)Object._set_(self,instance,value)Object._delete_(self,instance)

2 初始化和銷毀

#!/usr/local/bin/python3.6#coding:utf-8classX:def__init__(self,name):self.name=nameself.x=10print ("init? instance")def__del__(self):? ? ? ? print ('delete {}'.format(self.name))a=X('a')del? a# 因為python自身的垃圾回收機制燎竖,而del刪除實例的操作不確定何時執(zhí)行,因此需要使用del進行清除處理

結(jié)果如下?

3 hash

1 簡介

hash 中最基礎(chǔ)的hash就是取模運算要销。

list 不能hash的原因?

list 源碼: 其中hash=None,在調(diào)用None的時候自然是不能hash的

判斷是否可hash

#!/usr/local/bin/python3.6#coding:utf-8fromcollectionsimportHashableclassX:def__init__(self,x):self.x=xdef__hash__(self):return1print(isinstance(X(1),Hashable))print(isinstance([],Hashable))

結(jié)果如下?

2 定義不可哈希類型

#!/usr/local/bin/python3.6#coding:utf-8fromcollectionsimportHashableclassX:def__init__(self):self.x=10__hash__=Noneprint(isinstance(X(),Hashable))#判斷是否可hash构回,返回為bool類型print(hash(X()))

結(jié)果如下?

3 實例

實例如下

#!/usr/local/bin/python3.6#coding:utf-8classX:def__init__(self):self.x=10def__hash__(self):# 此處是定義的是實例的hash,和類沒關(guān)系returnNone# 此處設(shè)置hash的返回為None,模擬列表# 另一種方式定義不可哈希類型? __hash__=NoneclassY:#此類未設(shè)置相關(guān)的hash屬性def__init__(self):passclassZ:# 此類定義了hash的返回值為1 疏咐,則實例化后調(diào)用hash返回為1def__hash__(self):return1print(hash(Y()))# 此處返回整數(shù)print(hash(Z()))# 此處返回為固定數(shù)print(hash(X()))#進行判斷是否能夠進行hash操作纤掸,未進行hash,直接拋出異常

結(jié)果如下?

#!/usr/local/bin/python3.6#coding:utf-8classX:def__init__(self):self.x=10def__hash__(self):return1lst=[X(),X()]# 此處進行實例化并裝載到列表中print(lst)# 此處打印列表的值,指定的是內(nèi)存地址s=set(lst)# 此處定義一個集合凳鬓,集合本身具有去重功能茁肠,上述的hash的返回值是相同的,則說明其hash相同缩举,則表明其key相同垦梆,常理推論可得# 其會走set的去重屬性進行處理print(len(s))foriins:# 打印其中的值,如下print(hash(i))

結(jié)果如下

此處s集合中的元素hash后的結(jié)果是相同的仅孩,但是其卻沒有進行去重操作秀又,則此處表明hash相等的函數(shù)不一定就是相同的桐绒,此處沒有直接的相等關(guān)系

#!/usr/local/bin/python3.6#coding:utf-8classX:def__init__(self):self.x=10def__hash__(self):return1def__eq__(self, other):# 此處是判斷是否相等的原因,及就是其值必須相等的情況下才能說是同一個,而不是hash相同就認為是同一個,此處返回bool值情屹,當然可以是0或非0的數(shù)returnTruelst=[X(),X()]# 此處進行實例化并裝載到列表中print(lst)# 此處打印列表的值,指定的是內(nèi)存地址s=set(lst)# 此處定義一個集合军掂,集合本身具有去重功能嫡意,# 其會走set的去重屬性進行處理print(len(s))foriins:# 打印其中的值秆撮,如下print(hash(i))

結(jié)果如下?

#!/usr/local/bin/python3.6#coding:utf-8classX:def__init__(self):self.x=10def__hash__(self):return1def__eq__(self, other):# 此處是判斷是否相等的原因,及就是其值必須相等的情況下才能說是同一個船侧,而不是hash相同就認為是同一個returnFalselst=[X(),X()]# 此處進行實例化并裝載到列表中print(lst)# 此處打印列表的值,指定的是內(nèi)存地址s=set(lst)# 此處定義一個集合欠气,集合本身具有去重功能,上述的hash的返回值是相同的镜撩,則說明其hash相同预柒,則表明其key相同,常理推論可得# 其會走set的去重屬性進行處理print(len(s))foriins:# 打印其中的值,如下print(hash(i))

結(jié)果如下

#!/usr/local/bin/python3.6#coding:utf-8classX:def__init__(self):self.x=10def__hash__(self):return1def__eq__(self, other):# 此處是判斷是否相等的原因宜鸯,及就是其值必須相等的情況下才能說是同一個憔古,而不是hash相同就認為是同一個returnFalsea=X()lst=[a,a]print(lst)s=set(lst)print(len(s))forxins:print(hash(x))

結(jié)果如下

4 結(jié)論

set判斷是否是同一個的步驟:

1 先進行內(nèi)存地址的判斷,及is判斷淋袖,若內(nèi)存地址相同鸿市,則肯定是同一個

2 若內(nèi)存地址不同,再進行eq 判斷适贸,及就是==進行判斷,若相同灸芳,則是同一個,若不同拜姿,則不是同一個,此處和hash沒有任何關(guān)系

及就是: 同假為假冯遂,一真一假為真蕊肥,同真為真

默認的能夠使用hash的原因是由于在基類object中實現(xiàn)了hash方法,一般的蛤肌,不同的內(nèi)存地址的hash是不相同的壁却,兩個對象的hash相同,叫hash沖突

hash 相同不代表一樣

#!/usr/local/bin/python3.6#coding:utf-8classX:def__init__(self,x):self.x=xdef__hash__(self):return1def__eq__(self, other):# 此處是判斷是否相等的原因裸准,及就是其值必須相等的情況下才能說是同一個展东,而不是hash相同就認為是同一個returnself.x==other.x#? 此處表示二元操作,前面調(diào)用方法self.x炒俱,后面的作為參數(shù)傳遞進去進行處理盐肃,other表示第二個對象對應(yīng)的方法print? (hash(X(4)))lst=[X(4),X(6)]t=tuple(lst)s=set(lst)print (s)forxins:print? (hash(x))

結(jié)果如下

__hash__方法只是返回一個hash值作為set的key,但是去重,還需要__eq__來判斷2個對象是否相等权悟,

5 練習(xí):

設(shè)計二維坐標類Point砸王,比較2個坐標是否相等

#!/usr/local/bin/python3.6#coding:utf-8classPoint:def__init__(self,x,y):self.x=xself.y=ydef__hash__(self):returnhash((self.x,self.y))# 此處返回是一個元組,是不可變數(shù)據(jù)類型峦阁,此處可直接進行hashdef__eq__(self, other):returnself.x==other.xandself.y == other.ya1=Point(3,4)a2=Point(3,4)print ( a1 is a2 )print (a1==a2)

結(jié)果如下

4 bool

1簡介

_bool_?內(nèi)建函數(shù)bool(), 或者對象放在邏輯表達式的位置谦铃,調(diào)用這個函數(shù)返回布爾值,沒有定義_bool_榔昔,就找_len_?返回長度驹闰,非0為真,如果__len__也沒有定義撒会,則所有的實例都返回是真嘹朗。

2 實例

#!/usr/local/bin/python3.6#coding:utf-8classPoint:# 此類未定義len和bool,因此其返回值為恒真def__init__(self):self.x=3self.y=4# def __bool__(self):#? ? return Falseprint (bool(Point()))

結(jié)果如下

#!/usr/local/bin/python3.6#coding:utf-8classPoint:def__init__(self):self.x=3self.y=4def__bool__(self):# 此處定義了bool的返回值為False茧彤,則調(diào)用bool()返回結(jié)果應(yīng)該為FalsereturnFalseprint (bool(Point()))

#!/usr/local/bin/python3.6#coding:utf-8classPoint:def__init__(self):self.x=3self.y=4# def __bool__(self): # 此處定義了bool的返回值為False骡显,則調(diào)用bool()返回結(jié)果應(yīng)該為False#? ? return Falsedef__len__(self):# 此處用于當bool不存在時的找尋位置,為0則表示為空,則為Falsereturn0print (bool(Point()))

#!/usr/local/bin/python3.6#coding:utf-8classPoint:def__init__(self):self.x=3self.y=4def__bool__(self):# 同時存在惫谤,則以bool為準 returnFalsedef__len__(self):return1print (bool(Point()))

結(jié)果如下

這也就是為啥空的字典和空集合以及空列表為False的原因了壁顶,因為其沒有定義bool,因此其只能通過訪問len來實現(xiàn)了 溜歪。

5 可視化

1 簡介

方法意義

_repr_內(nèi)建函數(shù)repr()對一個對象獲取字符串表達式若专,如果一個類定義了_repr__但沒有定義_str\,那么在請求該類的實例的"非正式"的字符串也將調(diào)用_repr_()

_str_str() 函數(shù)蝴猪,內(nèi)建函數(shù)format调衰,print()函數(shù)調(diào)用,需要返回對象的字符串表達式

_bytes_bytes 的時候自阱,返回一個獨享的bytes表達嚎莉,及返回bytes對象

2 基礎(chǔ)實例

#!/usr/local/bin/python3.6#coding:utf-8classPoint:def__init__(self):self.x=3self.y=4def__repr__(self):returnstr([self.x,self.y])#此處的返回必須使用字符串進行包裹,否則會報錯print (Point())

結(jié)果如下?

#!/usr/local/bin/python3.6#coding:utf-8classPoint:def__init__(self):self.x=3self.y=4def__repr__(self):returnstr([self.x,self.y])#此處的返回必須使用字符串進行包裹沛豌,否則會報錯def__str__(self):# 若存在此屬性趋箩,則上述的表達式將不會被調(diào)用return'abcdefg'print (Point())

結(jié)果如下

#!/usr/local/bin/python3.6#coding:utf-8classPoint:def__init__(self):self.x=3self.y=4def__repr__(self):returnstr([self.x,self.y])#此處的返回必須使用字符串進行包裹,否則會報錯def__str__(self):# 若存在此屬性加派,則上述的表達式將不會被調(diào)用return'abcdefg'print (Point())p1=Point()p2=Point()lst=[p1,p2]forxinlst:print (x)print (lst)

#!/usr/local/bin/python3.6#coding:utf-8classPoint:def__init__(self):self.x=3self.y=4def__repr__(self):returnstr([self.x,self.y])#此處的返回必須使用字符串進行包裹叫确,否則會報錯def__str__(self):# 若存在此屬性,則上述的表達式將不會被調(diào)用return'abcdefg'print (Point())p1=Point()p2=Point()lst=(p1,p2)forxinlst:print (x)print (lst)print (*lst)#進行解包處理芍锦,此時是針對于對象上的竹勉,此時應(yīng)該調(diào)用的是str

3 結(jié)論

上述實例證明,當str和repr同時存在時娄琉,如果輸出結(jié)果直接作用于對象上次乓,則調(diào)用str方法,否則將調(diào)用repr方法

6 運算符重載

1 簡介

operator 模塊提供以下的特殊方法车胡,可以將類的實例使用下面操作符來進行操作

運算符特殊方法含義

<,<=,==,>,>=,!=_lt_,_le_,_eq_,_gt_,_ge_,_ne_比較運算符

+,-,*,/,%,//,**,divmod_add_,_sub_,_mul_,_truediv_,_mod_,_floordiv_,_pow_,_divmod_算數(shù)運算符檬输,移位,位運算也有對應(yīng)的方法

+=,-=,*=,/=,%=,//=,**=_iadd_,_isub_,_imul_,_itruediv_,_imod_,_ifloordiv_,_ipow_

2 實驗

#!/usr/local/bin/python3.6#coding:utf-8classA:def__init__(self,x):self.x=xdef__lt__(self, other):returnself.x? < other.xdef__eq__(self, other):returnself.x == other.xdef__ne__(self, other):returnself.x? != other.xdef__sub__(self, other):returnself.x - other.xprint (A(10)

結(jié)果如下

#!/usr/local/bin/python3.6#coding:utf-8classA:def__init__(self,x):self.x=xdef__iadd__(self, other):# 此處定義的是+= 返回的是self=self+otherself.x += other.x? ? ? ? print ('__iadd__')returnself# 此處返回的是一個實例匈棘,可通過調(diào)用其方法來實現(xiàn)此方法是否執(zhí)行a1=A(10)a2=A(20)print ('*'*30)a1+=a2print (a1.x,a2.x)

#!/usr/local/bin/python3.6#coding:utf-8classA:def__init__(self,x):self.x=xdef__iadd__(self, other):# 此處定義的是+= 返回的是self=self+other# self.x += other.xprint ('__iadd__')returnA(self.x+other.x )#此處方法相同a1=A(10)a2=A(20)print ('*'*30)a1+=a2print (a1.x,a2.x)

#!/usr/local/bin/python3.6#coding:utf-8classA:def__init__(self,x):self.x=xdef__iadd__(self, other):# 此處定義的是+= 返回的是self=self+other# self.x += other.xprint ('__iadd__')returnA(self.x+other.x )#此處方法相同def__isub__(self, other):? ? ? ? print ('__isub__')self.x -= other.xreturnselfa1=A(10)a2=A(20)print ('*'*30)a1+=a2print (a1.x,a2.x)a1-=a2print (a1.x,a2.x)

functools.total_ordering 的應(yīng)用

默認的

#!/usr/local/bin/python3.6#coding:utf-8classA:def__init__(self,x):self.x=xdef__lt__(self, other):# 此處定義的是小于丧慈,現(xiàn)需要使用大于等于,則默認會報錯returnself.x? < other.xprint (A(1) >= A(2))

結(jié)果如下

#!/usr/local/bin/python3.6#coding:utf-8fromfunctoolsimporttotal_ordering@total_orderingclassA:def__init__(self,x):self.x=xdef__lt__(self, other):# 此處定義的是小于主卫,現(xiàn)需要使用大于等于逃默,則默認會報錯returnself.x? < other.xprint(A(1) >= A(2))print(A(1)==A(2))print(A(1) != A(2))

結(jié)果如下

3 總結(jié)

運算符重載的應(yīng)用場景

往往是面向?qū)ο髮崿F(xiàn)的類,需要做大量的運算簇搅,而運算符是這種運算在數(shù)學(xué)上最常見的表達方式完域,int 類中,幾乎實現(xiàn)了所有操作符瘩将,可以作為參考

7 容器相關(guān)方法

1 簡介

內(nèi)建方法含義

_len_內(nèi)建函數(shù)len()吟税,返回對象的長度(>=0的整數(shù))凹耙,其實即使吧對象當作容器類型來看,就如同list或dict肠仪,bool()函數(shù)調(diào)用的時候肖抱,如果沒有_bool_()方法,則會看_len_()方法是否存在异旧,存在返回非0為真意述,第三方庫中可能存在size,其和len的含義相同

_iter_迭代器時吮蛹,調(diào)用荤崇,返回一個新的迭代器對象

_contains_in成員運算符,沒有實現(xiàn)潮针,就調(diào)用__iter__方法遍歷

_getitem_實現(xiàn)self[key]訪問术荤,序列對象,key接受整數(shù)為索引然低,或者切片喜每,對于set和dict,key為hashable雳攘,key不存在時引KeyError異常

_setitem_和__getitem__的訪問相似,是設(shè)置值的方法

_missing_字典使用_getitem_()調(diào)用時枫笛,key不存在執(zhí)行該方法

2 實例

#!/usr/local/bin/python3.6#coding:utf-8classItem:def__init__(self,name,*args):self.name=nameself.lst=list(args)def__len__(self):returnlen(self.lst)def__iter__(self):returniter(self.lst)# 此處返回是一個迭代器,必須是一個迭代器def__add__(self, other):# 此處使用+ 號返回一個列表self.lst.append(other)returnselfdef__getitem__(self, index):# 此處應(yīng)用于列表時吨灭,表示為索引,此處應(yīng)用于字典時刑巧,表示keyifindex? > len(self.lst):? ? ? ? ? ? print ('Key Error')else:returnself.lst[index]def__setitem__(self, index, value):# 此處表示修改屬性列表中的值ifindex? > len(self.lst):? ? ? ? ? ? print ('Key Error')else:self.lst[index]=valuereturnself# def __missing__(self, key): # 此方法只能適用于字典的處理#? ? passdef__repr__(self):returnstr(self.lst)# 此處對其進行可視化處理a=Item('mysql',12,3,45,678,8909)print (len(a))# 此處調(diào)用了__iter__方法foriina:print (i)print ('++++++++++++++++')print (a[2])# 此處調(diào)用了__getitem__方法喧兄,用于獲取值a+10# 此處使用__add__方法進行加入,此處追加到列表的末尾print (a[-1])# 獲取列表的最后一個元素啊楚,則得到此值a[1]=20# 使用__setitem__方法修改屬性print (a[1])#返回對應(yīng)位置的值a+10+20+30+40# 此處進行連加操作吠冤,因為其add方法返回是self,因此每次賦值后都會增加print (a)

結(jié)果如下

8 可調(diào)用對象

1 簡介

在python中一切皆對象,函數(shù)也不例外?

可調(diào)用對象

方法

__call__類中出現(xiàn)該方法恭理,實例就可以像函數(shù)一樣調(diào)用拯辙,

可調(diào)用對象: 定義一個類,并實例化得到其實例颜价,將實例像函數(shù)一樣調(diào)用涯保。調(diào)用是實例的,不是類的周伦。

2 實例

#!/usr/local/bin/python3.6#coding:utf-8deffoo():print(foo.__module__,foo.__name__)foo.__call__()# 此處的方法和下面的相同夕春,皆是調(diào)用該函數(shù)foo()print(dir(foo))

結(jié)果如下

函數(shù)的可調(diào)用原因是函數(shù)實現(xiàn)了\call()方法?

#!/usr/local/bin/python3.6#coding:utf-8deffoo():print(foo.__module__,foo.__name__)print(foo.__call__)# 此處返回一個函數(shù)對象是一個wrapperfoo.__call__()# 此處的方法和下面的相同,皆是調(diào)用該函數(shù)foo()

結(jié)果如下?

#!/usr/local/bin/python3.6#coding:utf-8classA:def__init__(self):self.x=1def__call__(self, *args):# 此處的第一個是self专挪,表明其是給實例使用的及志,并不是給類使用的returnargs# 此處返回一個元組print (A()(12344))# 此處第一個括號是實例化片排,第二個是傳遞參數(shù)并調(diào)用實例

3 練習(xí)

利用封裝完成斐波那契額數(shù)列

方法1

#!/usr/local/bin/python3.6#coding:utf-8classA:def__call__(self,num):a,b=0,1foriinrange(num):print(b)? ? ? ? ? ? a,b=b,a+bA()(10)

結(jié)果如下

方法2

#!/usr/local/bin/python3.6#coding:utf-8classA:def__init__(self):self.lst=[1,1,2]def__call__(self,num):ifnum <3:returnself.lst[:num]else:foriinrange(num-3):self.lst.append(self.lst[-1]+self.lst[-2])returnself.lstprint (A()(10))

結(jié)果如下

添加功能如下

#!/usr/bin/poython3.6#conding:utf-8classA:def__init__(self):self.lst=[1,1,2]def__len__(self):returnlen(self.lst)def__call__(self,x):iflen(self.lst)? >x:returnself.lst[:x]foriinrange(2,x):self.lst.append(self.lst[i]+self.lst[i-1])returnself.lstdef__getitem__(self, item):ifitem <0:returnNoneiflen(self) >item:returnself.lst[item]def__iter__(self):returniter(self.lst)a=A()print (a(10))print (a[4])forxina:print (x)

結(jié)果如下

9 上下文管理

1 推導(dǎo)過程

文件IO操作可以對文件對象進行上下文管理,使用with...as語法?

推導(dǎo)過程

#!/usr/bin/poython3.6#conding:utf-8# 此處為默認的上下文管理# with? open('test')? as f:#? ? passclassA:passwithA()asf:pass

結(jié)果如下

提示需要添加 __enter__屬性

添加如下

#!/usr/bin/poython3.6#conding:utf-8# 此處為默認的上下文管理# with? open('test')? as f:#? ? passclassA:def__enter__(self):passwithA()asf:pass

結(jié)果如下?

提示需要添加 __exit__屬性

#!/usr/bin/poython3.6#conding:utf-8# 此處為默認的上下文管理# with? open('test')? as f:#? ? passclassA:def__enter__(self):passdef__exit__(self, exc_type, exc_val, exc_tb):passwithA()asf:pass

結(jié)果如下

#!/usr/bin/poython3.6#conding:utf-8classA:def__enter__(self):print('__enter__')def__exit__(self, exc_type, exc_val, exc_tb):print('__exit__')withA()asf:pass

由此圖可知速侈,其調(diào)用順序是先調(diào)用_enter_率寡,后調(diào)用_exit_

2 屬性

方法意義

_enter_進入于此對象相關(guān)的上下文,如果存在該方法锌畸,with語法會把該方法的返回值作為綁定到as字句中指定的變量上

_exit_退出與此對象的上下文

exit 中變量的含義:

1 exc_type: 異常類型勇劣,如果沒有異常,則返回是None

2 exc_tb:異常追蹤信息潭枣,如果沒有異常比默,則是None

3 exc_va :異常對應(yīng)的值,如果沒異常盆犁,則是None?

此處的return 用于壓制異常命咐,若此處是False,則會拋出異常,等效True 或 False

缺少了enter 進不去谐岁,缺少了exitc出不來

#!/usr/bin/poython3.6#conding:utf-8classA:def__init__(self):print('init instance')def__enter__(self):print('__enter__')return1def__exit__(self, exc_type, exc_val, exc_tb):print('__exit__')p=A()withpasf:# 此處的p是__enter__的返回值醋奠,是f的參數(shù),若此處__enter__無return伊佃,則默認返回為None窜司,無意義print(p==f)# 此處用于比較p和f的關(guān)系 print(pisf)print(p)print(f)

上述結(jié)論如下:

實例化對象的時候,并不會調(diào)用enter航揉,進入with語句塊會調(diào)用__enter__方法塞祈,然后執(zhí)行語句體,最后離開with語句塊的時候帅涂,調(diào)用__exit__方法

with 可以開啟一個上下文運行環(huán)境议薪,在執(zhí)行前做一些準備工作,執(zhí)行后做一些收尾工作媳友。

3 上下文管理對異常的處理方式

#!/usr/bin/poython3.6#conding:utf-8classA:def__init__(self):print('init instance')def__enter__(self):print('__enter__')return1def__exit__(self, exc_type, exc_val, exc_tb):print('__exit__')p=A()withpasf:# 此處的p是__enter__的返回值斯议,是f的參數(shù),若此處__enter__無return醇锚,則默認返回為None哼御,無意義raiseException('Error')# 此處拋出異常,一般的,拋出異常后搂抒,語句將不會再次執(zhí)行print(p==f)# 此處用于比較p和f的關(guān)系print(pisf)print(p)print(f)

結(jié)果如下

由此證明艇搀,當異常拋出時,exit對應(yīng)的語句仍然會被執(zhí)行求晶。

#!/usr/bin/poython3.6#conding:utf-8importsysclassA:def__init__(self):print('init instance')def__enter__(self):print('__enter__')return1def__exit__(self, exc_type, exc_val, exc_tb):print('__exit__')p=A()withpasf:# 此處的p是__enter__的返回值焰雕,是f的參數(shù),若此處__enter__無return芳杏,則默認返回為None矩屁,無意義sys.exit()# 此處的是直接退出print(p==f)# 此處用于比較p和f的關(guān)系print(pisf)print(p)print(f)

結(jié)果如下

上述證明辟宗,此處滿足上述清理工作,上下文管理非常安全吝秕,能夠保證變量的順利清除工作泊脐。

#!/usr/bin/poython3.6#conding:utf-8importsysclassA:def__init__(self):print('init instance')def__enter__(self):print('__enter__')returnselfdef__exit__(self, exc_type, exc_val, exc_tb):print('__exit__')print(exc_tb)#追蹤信息print(exc_type)# 類型print(exc_val)# 值return1# 此處設(shè)置為1 是壓制異常,不讓其出現(xiàn)p=A()withpasf:# 此處的p是__enter__的返回值烁峭,是f的參數(shù)容客,若此處__enter__無return,則默認返回為None约郁,無意義raiseException('Error1234454')print(p==f)# 此處用于比較p和f的關(guān)系print(pisf)print(p)print(f)

結(jié)果如下?

4 通過此方法進行函數(shù)執(zhí)行時長計算

之前的計算時長方式

#!/usr/bin/poython3.6#conding:utf-8importdatetimeimporttimeimportsysdefwapper(fn):def_wapper(*args,**kwargs):start_time=datetime.datetime.now()? ? ? ? ret? =? fn(*args,**kwargs)? ? ? ? delta=(datetime.datetime.now()-start_time).total_seconds()print("{} 函數(shù)的執(zhí)行時間為: {}".format(fn.__name__,delta))returnretreturn_wapper@wapperdefadd(x,y):time.sleep(2)returnx+yadd(4,5)

結(jié)果如下?

使用上下文管理的方式統(tǒng)計函數(shù)執(zhí)行時間

#!/usr/bin/poython3.6#conding:utf-8import? datetimeimport? timeclassTimer:def__init__(self,fn):self.fn=fndef__enter__(self):self.start_time=datetime.datetime.now()returnself.fn# 此處對應(yīng)的是as前面的值def__exit__(self, exc_type, exc_val, exc_tb):? ? ? ? delat=(datetime.datetime.now()-self.start_time).total_seconds()? ? ? ? print ("函數(shù){} 的執(zhí)行時間為: {}".format(self.fn.__name__,delat))return1defadd(x,y):returnx+yp=Timer(add)with? p? asf:# 此處調(diào)用的是__enter__的返回值缩挑,重命名為ftime.sleep(2)? ? print (f(4,5))

結(jié)果如下

5 類裝飾器的應(yīng)用

#!/usr/bin/poython3.6#conding:utf-8import? datetimeimport timefrom functools import? wrapsclassA:def__init__(self,fn):self.fn=fndef__call__(self,*args,**kwargs):#實例調(diào)用支持的方法self.start_time=datetime.datetime.now()? ? ? ? ret =self.fn(*args,**kwargs)? ? ? ? delta=(datetime.datetime.now()-self.start_time).total_seconds()? ? ? ? print ("{} 函數(shù)的執(zhí)行時間為: {}".format(self.fn.__name__,delta))returnret@A#add=A(add)defadd(x,y):? ? time.sleep(2)returnx+yprint? (add(10,20))

結(jié)果如下

6 進行屬性覆蓋如下

#!/usr/bin/poython3.6#conding:utf-8importdatetimeimporttimefromfunctoolsimportwrapsclassA:def__init__(self,fn):self.fn=fndef__call__(self,*args,**kwargs):#實例調(diào)用支持的方法self.start_time=datetime.datetime.now()? ? ? ? ret = self.fn(*args,**kwargs)? ? ? ? delta=(datetime.datetime.now()-self.start_time).total_seconds()print("{} 函數(shù)的執(zhí)行時間為: {}".format(self.fn.__name__,delta))returnret@A? #add=A(add)defadd(x,y):'''this is function'''time.sleep(2)returnx+yprint(add(10,20))print(add.__doc__)# 此處打印出文檔

結(jié)果如下

#!/usr/bin/poython3.6#conding:utf-8importdatetimeimporttimefromfunctoolsimportwrapsclassA:def__init__(self,fn):self.__doc__=fn.__doc__# 此處只能進行部分的屬性覆蓋操作self.__name__=fn.__name__? ? ? ? self.fn=fndef__call__(self,*args,**kwargs):#實例調(diào)用支持的方法self.start_time=datetime.datetime.now()? ? ? ? ret = self.fn(*args,**kwargs)? ? ? ? delta=(datetime.datetime.now()-self.start_time).total_seconds()print("{} 函數(shù)的執(zhí)行時間為: {}".format(self.fn.__name__,delta))returnret@A? #add=A(add)defadd(x,y):'''this is function'''time.sleep(2)returnx+yprint(add(10,20))print(add.__doc__)# 此處打印出文檔

結(jié)果如下

#!/usr/bin/poython3.6#conding:utf-8importdatetimeimporttimefromfunctoolsimportwrapsclassA:def__init__(self,fn):wraps(fn)(self)# 調(diào)用此方法完成屬性的覆蓋操作,此處第一個是原函數(shù)鬓梅,后面是現(xiàn)在的函數(shù)self.fn=fndef__call__(self,*args,**kwargs):#實例調(diào)用支持的方法self.start_time=datetime.datetime.now()? ? ? ? ret = self.fn(*args,**kwargs)? ? ? ? delta=(datetime.datetime.now()-self.start_time).total_seconds()print("{} 函數(shù)的執(zhí)行時間為: {}".format(self.fn.__name__,delta))returnret@A? #add=A(add)defadd(x,y):'''this is function'''time.sleep(2)returnx+yprint(add(10,20))print(add.__doc__)# 此處打印出文檔

結(jié)果如下

7 contextlib.contextmanager

是一個裝飾器實現(xiàn)上下文管理供置,裝飾一個函數(shù),而不像類一樣可以實現(xiàn)__enter__和__exit__方法

對下面的函數(shù)有要求绽快,必須有yield芥丧,也就是這個函數(shù)必須返回一個和生成器,且只有一個yield的值坊罢。

#!/usr/bin/poython3.6#conding:utf-8importcontextlib@contextlib.contextmanagerdeffoo():print('enter')yieldprint('exit')withfoo()asf:pass

結(jié)果如下

#!/usr/bin/poython3.6#conding:utf-8importcontextlib@contextlib.contextmanagerdeffoo():print('enter')yield[1,2,34,56,5867,856,867,]# 此處相當分界線续担,用于分割上面和下面的執(zhí)行print('exit')withfoo()asf:print(f)

#!/usr/bin/poython3.6#conding:utf-8importcontextlib@contextlib.contextmanagerdeffoo():print('enter')yield[1,2,34,56,5867,856,867,]# 此處相當分界線,用于分割上面和下面的執(zhí)行print('12344exit')withfoo()asf:try:raiseExceptionfinally:print('exit')print(f)

結(jié)果如下

修改異常捕獲如下

#!/usr/bin/poython3.6#conding:utf-8importcontextlib@contextlib.contextmanagerdeffoo():print('enter')try:yield[1,2,34,56,5867,856,867,]# 此處相當分界線活孩,用于分割上面和下面的執(zhí)行finally:# 放置在此處能夠執(zhí)行后面的相關(guān)操作print('12344exit')withfoo()asf:raiseExceptionprint(f)

結(jié)果如下?

總結(jié): 如果業(yè)務(wù)邏輯簡單可以使用函數(shù)加裝飾器的方式赤拒,如果業(yè)務(wù)復(fù)雜,用類的方式加__enter__和__exit__更方便

8 上下文的應(yīng)用場景

1 增強功能

在代碼執(zhí)行的前后增加代碼诱鞠,以增強其功能,類似裝飾器的功能

2 資源管理?

打開了資源需要關(guān)閉这敬,例如文件對象航夺,網(wǎng)絡(luò)鏈接,數(shù)據(jù)庫鏈接等

3 權(quán)限驗證

在執(zhí)行代碼之前崔涂,做權(quán)限的驗證阳掐,在enter?中處理

在代碼進入的時候進行處理,在權(quán)限出去則不管

10 反射

1 概述

運行時:區(qū)別于編譯時冷蚂,指的是程序被加載到內(nèi)存中執(zhí)行的時候缭保。

反射:python中,能夠通過一個對象蝙茶,找出其type,class,attribute或method的能力艺骂,成為反射或自醒。

具有反射能力的函數(shù)type(),isinstance(),callable()(查看對象能否被調(diào)用),dir(),getattr()

2 內(nèi)建函數(shù)

object可以是類或?qū)嵗?語法格式:getattr(object,name[,default]) : 通過name 返回object的屬性值隆夯,當屬性不存在時钳恕,將使用default返回别伏,如果沒有default,則拋出attributeError忧额,name 必須位字符串 setattr(object,name,value)object的屬性存在厘肮,則覆蓋,若不存在睦番,則新增类茂。hasattr(object,name)? 判斷對象是否有這個名字屬性,name必須時字符串

3 實例

#!/usr/bin/poython3.6#conding:utf-8classA:x=10def__init__(self):self.x=5setattr(A,'y',20)# 動態(tài)添加類屬性位y=20print(A.__dict__)# 打印屬性信息列表a=A()setattr(a,'z',100)# 實例動態(tài)增加屬性print(getattr(A,'y'))# 查看增加的屬性是否存在print(getattr(A,'x'))# 定義屬性是否存在print(getattr(a,'y'))# 查看實例中是否存在該屬性print(a.__dict__)# 查看實例屬性信息中是否具有'z'屬性print(A.__dict__)# 打印屬性信息列表托嚣,此處查看當實例屬性信息增加后巩检,類屬性信息是否增加ifhasattr(a,'z'):print("{} 函數(shù)存在此屬性 {}".format('a','y'))

結(jié)果如下?

進階

#!/usr/bin/poython3.6#conding:utf-8classA:x=10def__init__(self,y):self.x=5self.y=ya=A(20)setattr(A,'printf',lambdaself:1)#此處增加一個類的屬性,并通過參數(shù)調(diào)用setattr(a,'myclass',lambda:10)# 此處增加一個實例屬性print(a.printf())# 打印此類的屬性結(jié)果如下print(getattr(a,'printf')())# 因為此處是函數(shù)注益,因此需要通過()來進行調(diào)用print(getattr(a,'myclass')())ifnothasattr(A,'sub'):# 此處添加一個類的函數(shù)屬性碴巾,實現(xiàn)函數(shù)之前的差運算setattr(A,'sub',lambdaself,other : A(self.y- other.y) )ifnothasattr(A,'add'):# 此處添加一個類的屬性,實現(xiàn)函數(shù)之間的和的計算setattr(A,'add',lambdaself,other: (self.y + other.y))print(a.__dict__)print(A.__dict__)b1=A(10)b2=A(20)print(b2.sub(b1))print(b1.add(b2))

結(jié)果如下

注意:

這種動態(tài)增加屬性的方式是運行時改變類或者實例的方式丑搔,比裝飾器和Mixin更加靈活厦瓢,其具有更大的使用用途

4 實例應(yīng)用

實現(xiàn)分發(fā)器

簡單雛形

#!/usr/bin/poython3.6#conding:utf-8classDispatcher:defcmd1(self):# 此處在內(nèi)部定義一個方法print ('cmd10')defrun(self):# 此處用于執(zhí)行whileTrue:cmd=input("plase? input str:")#退出程序命令ifcmd.strip() =='q'orcmd.strip() =='quit':returngetattr(self,cmd.strip(),self.__defaltfun)()# 此處用于獲取該方法,若不存在啤月,則執(zhí)行默認程序def__defaltfun(self):? ? ? ? print ('default')a=Dispatcher()a.run()

結(jié)果如下?

#!/usr/bin/poython3.6#conding:utf-8classDispatcher:defcmd1(self):# 此處在內(nèi)部定義一個方法print ('cmd10')defreg(self,cmd,fn):ifisinstance(cmd,str):? ? ? ? ? ? setattr(self.__class__,cmd.strip(),fn)# 此處使用的是類煮仇,若是實例化,則需要進行下面將self傳入進去的方式進行調(diào)用else:print ('TypeError')defrun(self):whileTrue:cmd = input("plase? input str:")ifcmd.strip()? =='q'orcmd.strip() =='quit':returngetattr(self,cmd.strip(),self.defaultfun)()defdefaultfun(self):? ? ? ? print ('default')a=Dispatcher()a.reg('cmd2',lambdaself:print(2)) a.reg('cmd3',lambdaself:print(3))a.run()

結(jié)果如下

5 反射相關(guān)魔術(shù)方法

魔術(shù)方法意義

_getattr_()當通過搜索實例谎仲,實例的類以及祖先類查不到的屬性浙垫,就會調(diào)用此方法

_setattr_()通過訪問實例屬性,進行增加郑诺,修改都要調(diào)用它

_delattr_()當通過實例刪除屬性的時候調(diào)用此方法

_getattribute_實例所有的屬性調(diào)用都是從政方法開始

實例如下:

#!/usr/bin/poython3.6#conding:utf-8classBase:a=5classA(Base):m=6def__init__(self,x):print('init')? ? ? ? self.x=x#此處定義了屬性夹姥,所以下面的__setattr__被執(zhí)行了一次,初始化先執(zhí)行辙诞,之后__setattr__這個屬性再執(zhí)行一次def__getattr__(self, item):#針對上述無法查找到的屬性辙售,則執(zhí)行此屬性,可配置其值為None來彌補此屬性值print('__getattr__',item)? ? ? ? self.__dict__[item]=Nonedef__setattr__(self, key, value):#設(shè)置一個屬性時飞涂,一定要執(zhí)行旦部,至于是否生效,則另當別論print('__setattr__',key,value)def__delattr__(self, item):#此處在刪除一個實例的屬性進行的操作较店,只要實例能找到士八,都能夠刪除print('__delattr__',item)A.n=50# 此處是正常的添加類屬性,不會產(chǎn)生其他的報錯a=A(20)print(a.__dict__)a.b# 針對不存在的屬性進行調(diào)用a.x=30# 設(shè)置實例的屬性變化a.c=200#添加一個不存在的屬性dela.a# 刪除一個實例的屬性print(a.__dict__)

結(jié)果如下

#!/usr/bin/poython3.6#conding:utf-8classBase:a=5classA(Base):m=6def__init__(self,x):print('init')? ? ? ? self.x=x#此處定義了屬性梁呈,所以下面的__setattr__被執(zhí)行了一次婚度,初始化先執(zhí)行,之后__setattr__這個屬性再執(zhí)行一次def__getattr__(self, item):#針對上述無法查找到的屬性捧杉,則執(zhí)行此屬性陕见,可配置其值為None來彌補此屬性值print('__getattr__',item)? ? ? ? self.__dict__[item]=Nonedef__setattr__(self, key, value):#設(shè)置一個屬性時秘血,一定要執(zhí)行,至于是否生效评甜,則另當別論print('__setattr__',key,value)def__delattr__(self, item):#此處在刪除一個實例的屬性進行的操作灰粮,只要實例能找到,都能夠刪除print('__delattr__',item)def__getattribute__(self, item):#此處是在字典屬性之前進行攔截執(zhí)行print('__getattribute__',item)a=A(20)print(a.x)

結(jié)果如下?

實例的所有屬性的訪問忍坷,第一個都會調(diào)用__getattribute__方法粘舟,其阻止了屬性查找,該方法應(yīng)該返回(計算后)值或者拋出一個attributeError 異常

他的return 值將作為屬性查找的結(jié)果佩研,如果拋出attributeError 異常柑肴,則會直接調(diào)用__getattr__方法,因為表示屬性沒有找到

#!/usr/bin/poython3.6#conding:utf-8classBase:a=5classA(Base):m=6def__init__(self,x):print('init')? ? ? ? self.x=x#此處定義了屬性旬薯,所以下面的__setattr__被執(zhí)行了一次晰骑,初始化先執(zhí)行,之后__setattr__這個屬性再執(zhí)行一次def__getattr__(self, item):#針對上述無法查找到的屬性绊序,則執(zhí)行此屬性硕舆,可配置其值為None來彌補此屬性值print('__getattr__',item)# self.__dict__[item]=Nonedef__setattr__(self, key, value):#設(shè)置一個屬性時,一定要執(zhí)行骤公,至于是否生效抚官,則另當別論print('__setattr__',key,value)def__delattr__(self, item):#此處在刪除一個實例的屬性進行的操作,只要實例能找到阶捆,都能夠刪除print('__delattr__',item)def__getattribute__(self, item):#此處是在字典屬性之前進行攔截執(zhí)行print('__getattribute__',item)raiseAttributeError(item)# 此處若屬性不存在凌节,拋出異常,則直接進入getattr中機型處理a=A(20)print(a.x)

#!/usr/bin/poython3.6#conding:utf-8classBase:a=5classA(Base):m=6def__init__(self,x):print('init')? ? ? ? self.x=x#此處定義了屬性洒试,所以下面的__setattr__被執(zhí)行了一次倍奢,初始化先執(zhí)行缺猛,之后__setattr__這個屬性再執(zhí)行一次def__getattr__(self, item):#針對上述無法查找到的屬性匪凉,則執(zhí)行此屬性,可配置其值為None來彌補此屬性值print('__getattr__',item)# self.__dict__[item]=Nonedef__setattr__(self, key, value):#設(shè)置一個屬性時醉途,一定要執(zhí)行捕犬,至于是否生效,則另當別論print('__setattr__',key,value)def__delattr__(self, item):#此處在刪除一個實例的屬性進行的操作酵镜,只要實例能找到碉碉,都能夠刪除print('__delattr__',item)def__getattribute__(self, item):#此處是在字典屬性之前進行攔截執(zhí)行print('__getattribute__',item)# raise? AttributeError(item)? # 此處若屬性不存在,拋出異常淮韭,則直接進入getattr中機型處理returnobject.__getattribute__(self,item)#此處表示若不存在垢粮,則直接去object中進行查找,并得到其訪問的值a=A(20)print(a.b)

結(jié)果如下?

注意:\getattribute\ 方法中為了避免在該方法中無限遞歸靠粪,實現(xiàn)了應(yīng)該永久調(diào)用基類的同名方法以訪問需要的任何屬性蜡吧,除非你明確知道\getattribute\方法用來做什么毫蚓,否則不要使用它。

6 總結(jié):

屬性查找順序

實例調(diào)用----> \getattribute()----> instance.\dict---->instance.\class----> 繼承的祖先類(知道object)的\dict\調(diào)用\getattr()

11 描述器

1 定義

在python中昔善,一個類實現(xiàn)了一下三種方式中的任何一種元潘,就是描述器

object.__get__(self,instance,owner)object.__set__(self,instance,value)object.__delete__(self,instance)

如果僅實現(xiàn)了\get\,就是非數(shù)據(jù)描述器 non-data descriptor?

同時實現(xiàn)了\get\和\set\或者\get\和\delete\ 或者三個都實現(xiàn)君仆,則稱為數(shù)據(jù)描述符 data descriptor?

如果一個類的類屬性設(shè)置為描述器翩概,那么那被稱為owner屬主。

2 基本實踐

#!/usr/bin/poython3.6#conding:utf-8classA:def__init__(self):print('A,init')? ? ? ? self.a1='a1'classB:x=A()# 調(diào)用上述的類形成實例def__init__(self):print('B,init')? ? ? ? self.x=100# 此處B類實例的屬性為x=100print(B.x.a1)# 此處通過調(diào)用B類而調(diào)用B類的類屬性x返咱,進而調(diào)用A類的實例的a1方法.必須是先初始化钥庇,然后再進行相關(guān)的調(diào)用b=B()# 此處調(diào)用從類開始,會執(zhí)行A和B的init方法print(b.x)#此處調(diào)用的是實例B的x屬性咖摹,其值是100评姨,此處對x.a1沒有屬性,因為其被self.x=100覆蓋了

結(jié)果如下

默認查找順序: 類加載時萤晴,類變量需要先生成吐句,而類B的x屬性是類A的實例,因此需要先執(zhí)行類A的初始化硫眯,進而執(zhí)行B的初始化操作蕴侧。

#!/usr/bin/poython3.6#conding:utf-8classA:def__init__(self):? ? ? ? print ('A,init')self.a1='a1'def__get__(self, instance, owner):#加入此方法,行為被攔截两入,執(zhí)行了init后執(zhí)行了此方法净宵,返回為None,因此后面調(diào)用的Nonereturn(self,instance,owner)classB:x=A()# 調(diào)用上述的類形成實例def__init__(self):? ? ? ? print ('B,init')self.x=100# 此處B類實例的屬性為x=100print (B.x)# 此處x對應(yīng)的a1的屬性被攔截,上述返回為x實例裹纳,instance為B類實例的返回择葡,owner為B類,及就是屬性所屬的類剃氧,self為A類的實例b=B()# 對類B進行實例化print (b.x)# 對類b的屬性進行調(diào)用

結(jié)果如下

屬性中的值:self : 類A對應(yīng)的實例owner: 類B instance 說明敏储,及類B的實例

#!/usr/bin/poython3.6#conding:utf-8classA:def__init__(self):? ? ? ? print ('A,init')self.a1='a1'def__get__(self, instance, owner):#加入此方法,行為被攔截朋鞍,執(zhí)行了init后執(zhí)行了此方法已添,返回為None,因此后面調(diào)用的Nonereturn(self,instance,owner)classB:x=A()# 調(diào)用上述的類形成實例def__init__(self):? ? ? ? print ('B,init')self.x=A()# 此處B類實例的屬性為調(diào)用A類的屬性b=B()# 對類B進行實例化print (b.x.a1)# 對類b的屬性進行調(diào)用,此處調(diào)用的是A類的屬性,此處沒有觸動__get__魔術(shù)方法滥酥,進而說明__get__和實例無關(guān)

結(jié)論: _get_()魔術(shù)方法只對調(diào)用的類有攔截作用更舞,對類B下的實例無任何作用,此get是在調(diào)用子類的類屬性時會執(zhí)行此方法坎吻。

通過屬性描述器操作屬主的狀態(tài)

#!/usr/bin/poython3.6#conding:utf-8classA:def__init__(self):print('A,init')? ? ? ? self.a1='a1'def__get__(self, instance, owner):#加入此方法缆蝉,行為被攔截,執(zhí)行了init后執(zhí)行了此方法,返回為None,因此后面調(diào)用的Nonereturnself# 此處返回self,則表示A的實例刊头,A的實例當然可以調(diào)用a1方法classB:x=A()# 調(diào)用上述的類形成實例def__init__(self):print('B,init')? ? ? ? self.x=A()# 此處B類實例的屬性為調(diào)用A類的屬性print(B.x.a1)# 此處因為返回的是self,及A的實例黍瞧,因此此處可以調(diào)用A實例的a1方法,自然是成功的B.x.a1=30#通過描述器來修改屬主的狀態(tài)print(B.x.a1)# 打印狀態(tài)

結(jié)果如下

此處通過返回為self的方式來達到調(diào)用類B的屬性來調(diào)用類A的實例屬性的目的原杂。

#!/usr/bin/poython3.6#conding:utf-8classA:def__init__(self):print('A,init')? ? ? ? self.a1='a1'def__get__(self, instance, owner):#加入此方法印颤,行為被攔截,執(zhí)行了init后執(zhí)行了此方法污尉,返回為None,因此后面調(diào)用的Noneprint('__get__',self,instance,owner)returnself# 此處返回self,則表示A的實例膀哲,A的實例當然可以調(diào)用a1方法# def? __set__(self, instance, value): #實例化B類時需要調(diào)用此方法,#? ? print ('__set__',self,instance,value)classB:x=A()# 調(diào)用上述的類形成實例def__init__(self):print('B,init')? ? ? ? self.x=100# 此處B類實例的屬性為調(diào)用A類的屬性b=B()print(b.__dict__)# 打印實例b對應(yīng)的屬性字典print('+'*30)print(b.x.a1)#此處默認的a1方法是不存在于b實例中被碗,使用set方法將跳過b中定義的self.x方法

結(jié)果如下

使用_set_()方法如下

#!/usr/bin/poython3.6#conding:utf-8classA:def__init__(self):print('A,init')? ? ? ? self.a1='a1'def__get__(self, instance, owner):#加入此方法某宪,行為被攔截,執(zhí)行了init后執(zhí)行了此方法锐朴,返回為None,因此后面調(diào)用的Noneprint('__get__',self,instance,owner)returnself# 此處返回self,則表示A的實例兴喂,A的實例當然可以調(diào)用a1方法def__set__(self, instance, value):#實例化B類時需要調(diào)用此方法,此處是將B的實例和A的實例一起送進了set方法中焚志,value及就是B類定義的實例的屬性對應(yīng)的值print('__set__',self,instance,value)classB:x=A()# 調(diào)用上述的類形成實例def__init__(self):print('B,init')? ? ? ? self.x=100# 此處B類實例的屬性為調(diào)用A類的屬性b=B()print(b.__dict__)# 打印實例b對應(yīng)的屬性字典print('+'*30)print(b.x.a1)

結(jié)果如下

如下

#!/usr/bin/poython3.6#conding:utf-8classA:def__init__(self):print('A,init')? ? ? ? self.a1='a1'def__get__(self, instance, owner):#加入此方法衣迷,行為被攔截,執(zhí)行了init后執(zhí)行了此方法酱酬,返回為None,因此后面調(diào)用的Noneprint('__get__',self,instance,owner)returnself# 此處返回self,則表示A的實例壶谒,A的實例當然可以調(diào)用a1方法def__set__(self, instance, value):#實例化B類時需要調(diào)用此方法,print('__set__',self,instance,value)? ? ? ? self.a1=value# 若此處定義a1的返回值為value,及類B對應(yīng)的實例屬性x的值膳沽,則此處在訪問時汗菜,其結(jié)果便是100 classB:x=A()# 調(diào)用上述的類形成實例def__init__(self):print('B,init')? ? ? ? self.x=100# 此處B類實例的屬性為調(diào)用A類的屬性b=B()print(b.__dict__)# 打印實例b對應(yīng)的屬性字典print('+'*30)print(b.x.a1)# 此處最終訪問__get__的原因是此處調(diào)用的是類的屬性,而不是實例的屬性挑社,因此__get__會生效

3 結(jié)論如下:

當一個類的類屬性是一個數(shù)據(jù)描述器時(及除了\get\方法外還有至少一種方法)陨界,對他的實例屬性描述器的操作相當與對應(yīng)的類的屬性進行操作,及實例的字典優(yōu)先級會降低痛阻,而類的字典的優(yōu)先級會升高菌瘪,實際的結(jié)果是當其成為數(shù)據(jù)屬性描述器時,其對應(yīng)的實例的字典中定義的實例屬性將會消失

屬性查找順序:

實例的dict優(yōu)先于非數(shù)據(jù)描述器(只有\(zhòng)get\方法)阱当,數(shù)據(jù)描述器優(yōu)先于實例的\dict\?

及 數(shù)據(jù)描述器---> 實例的_dict_---> 非數(shù)據(jù)描述器---> 類的_dict_

4 python中描述器的應(yīng)用

描述器在python中應(yīng)用廣泛

python的方法包括(staticmethod)和classmethod() 都是通過非數(shù)據(jù)描述器來實現(xiàn)俏扩。因此實例可以重新定義和覆蓋,這允許單個實例獲取同一類的與其他實例不同的行為

property() 函數(shù)實現(xiàn)為一個數(shù)據(jù)描述器弊添。因此實例不能覆蓋其行為

init也是非數(shù)據(jù)描述器动猬,基本上的魔術(shù)方法都是飛數(shù)據(jù)描述器

5 練習(xí)

1 實現(xiàn) StaticMethod 裝飾器,實現(xiàn)staticmethod的部分功能

#!/usr/bin/poython3.6#conding:utf-8classStaticMethod:def__init__(self,fn):self.fn=fndef__get__(self, instance, owner):# 此方法是在調(diào)用類的屬性時存在的表箭,是在實例字典之后調(diào)用,類字典之前調(diào)用,及相當于類字典returnself.fnclassA:@StaticMethod#? a=StaticMethod(a)defa():? ? ? ? print ('123456')x=A()#類的實例化x.a()# 調(diào)用實例化的函數(shù)

結(jié)果如下?

2 實現(xiàn)ClassMethod 裝飾器免钻,完成 classmethod裝飾器的功能

#!/usr/bin/poython3.6#conding:utf-8classClassMethod:def__init__(self,fn):print(fn)? ? ? ? self.fn=fndef__get__(self, instance, owner):print(self,instance,owner)returnself.fnclassA:? ? @ClassMethoddefbar(cls):print(cls.__name__)f=A.barprint(f)f(A)# A.bar(A) #此處需要傳入函數(shù)參數(shù)A進行處理

結(jié)果如下

#!/usr/bin/poython3.6#conding:utf-8fromfunctoolsimportpartialclassClassMethod:def__init__(self,fn):print(fn)? ? ? ? self.fn=fndef__get__(self, instance, owner):print(self,instance,owner)returnpartial(self.fn,owner)# 偏函數(shù)彼水,使用此函數(shù)進行構(gòu)建新函數(shù)的操作,此處相當于將self.fn替換成了owner极舔,而owner為A類classA:? ? @ClassMethoddefbar(cls):print(cls.__name__)A.bar()

結(jié)果如下?

3 對實例的數(shù)據(jù)進行校驗

#!/usr/bin/poython3.6#conding:utf-8classTyped:def__init__(self, type):self.type = typedef__get__(self, instance, owner):? ? ? ? passdef__set__(self, instance, value):# 此處的value是類Person定義的類屬性中的類對應(yīng)的值,及就是name和age對應(yīng)的值ifnotisinstance(value,self.type):? ? ? ? ? ? print ("{} is? not {} this is? type {}".format(value,self.type,type(value)))? ? ? ? ? ? raise ValueError(value)classPerson:name=Typed(str)#通過此處定義type的方式完成對類型的檢測age=Typed(int)def__init__(self,name:str,age:int):self.name=nameself.age=agePerson('TOM',20)Person('jerry','30')

結(jié)果如下?

使用函數(shù)獲取參數(shù)簽名的方式來進行相關(guān)的判斷

#!/usr/bin/poython3.6#conding:utf-8import? inspectclassTyped:def__init__(self,type):self.type=typedef__get__(self, instance, owner):? ? ? ? passdef__set__(self, instance, value):ifnotisinstance(value,self.type):? ? ? ? ? ? print("{} is? not {} this is? type {}".format(value,self.type, type(value)))? ? ? ? ? ? raise ValueError(value)classTypeAssert:def__init__(self, cls):self.cls = clsdef__call__(self,name,age):? ? ? ? param = inspect.signature(self.cls).parametersfor_, vinparam.items():# 此處獲取參數(shù)屬性# print(v.name, v.annotation)ifv.annotation? !=v.empty:setattr(self.cls,name,Typed(v.annotation))#此處加入?yún)?shù)數(shù)據(jù)屬性至類中setattr(self.cls, age, Typed(v.annotation))@TypeAssertclassPerson:def__init__(self,name:str,age:int):self.name=nameself.age=agePerson('tom',40)Person('tom1','50')Person(40,60)

結(jié)果如下

4 模擬Property的功能

#!/usr/bin/poython3.6#conding:utf-8classProperty:# 數(shù)據(jù)描述器def__init__(self,fget,fset=None):#此處的fget傳遞的是dataself.fget=fgetself.fset=fsetdef__get__(self, instance, owner):# instance 是A類的實例ifinstance? isnotNone:returnself.fget(instance)# 此處是將data寫入returnselfdef__set__(self, instance, value):ifcallable(self.fset):#是否是可調(diào)用的self.fset(instance,value)else:raise? AttributeError('屬性異常')defsetter(self,fn):# 此處傳遞的是最底下的data參數(shù)凤覆,fn,self.fset=fn# return self.fset? # 此處若使用self.fset則表示返回的都是data.此處的data會做覆蓋拆魏,導(dǎo)致最終的結(jié)果是相同的returnself# 要想其是描述器盯桦,必須返回此值,否則返回是類A的屬性classA:def__init__(self,data):self.__data=data? ? @Property#data=Property(data)defdata(self):returnself.__data? ? @data.setter#data=data.setter(data),此處對應(yīng)的data是上述PROPERTY 傳入data生成實例的data,此處的方法是其方法defdata(self,value):self.__data=valuereturnself.__data

結(jié)果如下

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末渤刃,一起剝皮案震驚了整個濱河市拥峦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌卖子,老刑警劉巖略号,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異洋闽,居然都是意外死亡玄柠,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門诫舅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來羽利,“玉大人,你說我怎么就攤上這事刊懈≌饣。” “怎么了?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵俏讹,是天一觀的道長当宴。 經(jīng)常有香客問我,道長泽疆,這世上最難降的妖魔是什么户矢? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮殉疼,結(jié)果婚禮上梯浪,老公的妹妹穿的比我還像新娘。我一直安慰自己瓢娜,他們只是感情好挂洛,可當我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著眠砾,像睡著了一般虏劲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天柒巫,我揣著相機與錄音励堡,去河邊找鬼。 笑死堡掏,一個胖子當著我的面吹牛应结,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播泉唁,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼鹅龄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了亭畜?” 一聲冷哼從身側(cè)響起扮休,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎贱案,沒想到半個月后肛炮,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡宝踪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年侨糟,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瘩燥。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡秕重,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出厉膀,到底是詐尸還是另有隱情溶耘,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布服鹅,位于F島的核電站凳兵,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏企软。R本人自食惡果不足惜庐扫,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望仗哨。 院中可真熱鬧形庭,春花似錦、人聲如沸厌漂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽苇倡。三九已至富纸,卻和暖如春囤踩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背晓褪。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工高职, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人辞州。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像寥粹,于是被迫代替她去往敵國和親变过。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容