魔術方法
使用Python的魔術方法的最大優勢在于他們提供了一種簡單的方法來讓對象可以表現的像內置類型一樣。那意味著你可以避免丑陋的,違反直覺的,不標準的的操作方法。
特殊屬性
屬性
|
含義
|
__name__
|
類、函數、方法等名字
|
__module__
|
類定義所在的模塊名?
|
__class__
|
對象或類所屬的類
|
__bases__
|
類的基類的元組,順序為它們在基類列表中出現的順序
|
__doc__
|
類、函數的文檔字符串,如果沒有定義則為None
|
__mro__
|
類的mro,class.mro()返回的結果保存在__mro__中
|
__dict__
|
類或實例的屬性,可寫的字典
|
查看屬性
方法
|
意義
|
__dir__
|
返回類或者對象的所有成員名稱列表.dir()函數就是調用__dir__().如果提供了__dir__(),則返回屬性的列表,否則就會盡量從__dict__屬性中收集信息
|
- 如果dir([obj])參數包含方法__dir__(),該方法將被調用。如果參數不包含__dir__(),該方法將最大限度地收集參數信息。
- dir()對于不同類型的對象具有不同的行為
- 如果對象是模塊對象,列表包含模塊的屬性名
- 如果對象是類型或者類對象,列表包含類的屬性名,及它的積累的屬性名
- 否則,列表包含對象的屬性名,它的類的屬性名和類的基類的屬性名
魔術方法
- 分類:
- 創建與銷毀
- __init__與__del__
- hash
- bool
- 可視化
- 運算符重載
- 容器和大小
- 可調用對象
- 上下文管理
- 反射
- 描述器
- 其他雜項
- 創建與銷毀
方法
|
意義
|
__hash__
|
內建函數hash()調用的返回值,返回一個整數。如果定義這個方法該類的實例就可hash
|
- hash值相同不代表對象相同,比如hash算法為%3取模,此時3,6,9的hash值相同但是對象并不相同。
set去重會先判斷is是否相同,如果不同再判斷==是否相同,如都不同則會放到集合中
方法
|
意義
|
__eq__
|
對應==操作符,判斷2個對象是否相等返回bool值
|
- __hash__方法只是返回一個hash值作為set的key,但是去重,還需要__eq__來判斷對象是否相等。
- hash值相等,只是hash沖突,不能說明兩個對象是相等的
- 因此,一般來說hash方法是為了作為set或者dict的key,所以去重同時需要eq方法
- 可hash對象昂必須提供__hash__方法,沒有提供的話,instance(p1,collections.Hashable)一定為False。去重要提供__eq__方法。
- 練習:
- 設計二維坐標Point,比較2個坐標是否相等?
- 思考:為什么list不可以hash?

bool
- 四大皆空<list,tuple,set,dict>,__len__()為0所以必定為False
方法
|
意義
|
__bool__
|
內建函數bool(),或者對象放在邏輯表達式的位置,調用這個函數返回布爾值。沒有定義__bool__(),就找__len__()返回長度,非0為真。如果__len__()也沒有定義,那么所有實例都返回真
|
可視化
方法
|
意義
|
__repr__
|
內建函數repr()對一個對象獲取字符串表達。如果一個類定義了__repr__()但沒有定義__str__,那么在請求該類的‘非正式’的字符串表示時也將調用__repr__()
|
__str__
|
str()函數,內建函數format、print()函數調用,需要返回對象的字符串表達
|
__bytes__
|
bytes的時候,返回一個對象的bytes表達,即返回bytes對象
|
運算符重載
- operator模塊提供一下的特殊方法,可以將類的實例使用下面的操作符來操作
運算符
|
特殊方法
|
含義
|
<,<=,==,>,>=,!=
|
__lt__,__le__,__eq__,__gt__,__ge__,__ne__
|
比較運算符
|
+,-,*, /,%,//,**,divmod
|
__add__,__sub__,__mul__,__truediv__,__mod__,__floordiv__,__pow__,__divmod__
|
算數運算符,位運算符也有對應的方法
|
+=,-=,*=,/=,%=,//=,**=
|
__iadd__,__isub__,__imul__,__itruediv__,__imod__,__floordiv__,__ipow__
|
- 練習:完成Point類設計,實現判斷點相等的方法,并完成向量的加法
- 往往是用面向對象實現的類,需要做大量的運算,而運算符是這種在數學上最常見的表達方式。例如,上例中的對+進行了運算符重載,實現了Point類的二元操作,重新定義為Point+Point
- 提供運算符重載,比直接提供加法方法要更加適合該領域內使用者的習慣
- int類,幾乎實現了所有操作符
方法
|
意義
|
__len__
|
內建函數len(),返回對象的長度(>0的整數),其實即使把對象當做容器類型看,就如同list或者dict。bool()函數調用的時候,如果沒有__bool__()方法,則會看__len__()方法是否存在,存在返回非0為真
|
__iter__
|
迭代容器時,調用,返回一個新的迭代器對象
|
__contains__
|
in成員運算符,沒有實現,就調用__iter__方法遍歷
|
__getitem__
|
實現self[key]訪問。序列對象,key接受整數為索引,或者切片。對于set和dict,key為hashable。key不存在引發keyError異常
|
__setitem__
|
和__getitem__的訪問類似,是設置值得方法
|
__missing__
|
‘字典使用’__getitem__()調用時,key不存在執行該方法
|
- python中一切皆對象,函數也不例外
- 函數即對象,對象foo加上(),就是調用對象的__call__()方法
- 可調用對象
方法
|
意義
|
__call__
|
類中的第一個該方法,實例就可以像函數一樣調用
|
- 可調用對象:定義一個類,并實例化得到其實例,將實例像函數一樣調用。
- 練習:定義一個斐波那契數列的類,方便調用,計算第n項
上下文管理
- 文件IO操作可以對文件對象使用上下文管理,使用with…as語法
- 上下文管理對象
- 當一個對象同時實現了__enter__和__exit__()方法,它就屬于上下文管理的對象
方法
|
意義
|
__enter__
|
進入與此對象相關的上下文,如果存在該方法,with語法會把該方法的返回值作為綁定到as子句中指定的變量上
|
__exit__
|
退出與此對象相關的上下文
|
- 實例化對象的時候,并不會調用enter,進入with語句塊調用__enter__方法,然后執行語句體,最后離開with語句塊的時候,調用__exit__方法
- with可以開啟一個上下文運行環境,在執行前做一些準備,執行后做一些收尾工作
- 看看異常對上下文的影響,通過下例可以看出在enter和exit照樣執行,上下文管理是安全的
- 極端例子:調用sys.exit(),它會退出當前解釋器
- 打開Python解釋器,在里面敲sys.exit(),窗口直接關閉了,也就是說碰到這一句,Python運行環境直接退出了
- 從執行結果來看,依然執行了__exit__函數,哪怕是退出Python運行環境
- 由此說明上下文管理很安全
- 不相等的原因在于__enter__方法上,它將自己的返回值賦給了f,繼續修改測試看下例
- __enter__方法返回值就是上下文使用的對象,with語法會把它的返回值賦給as子句的變量
- __enter__方法和__exit__方法的參數
- __enter__方法沒有其他參數
- __exit__方法有三個參數(self.exctype,excvalue,traceback)
- 這三個參數都與異常有關
- 如果該上下文退出時沒有異常,這三個參數都是None
- 如果有異常,參數意義如下:
- exc_type:異常類型
- exc_value:異常的值
- traceback,異常的追蹤信息
- __exit方法返回一個等效True的值,則壓制異常;否則繼續拋出異常
- 練習:為加法函數計時
- 1.使用裝飾器顯示該函數的執行時長
- 2.使用上下文管理顯示該函數的執行時長
- 它是一個裝飾器實現上下文管理,裝飾一個函數,而不用像類一樣實現__enter__和__exit__方法
- 對下面的函數有要求,必須有yield,也就是說這個函數必須返回一個生成器,且只有yield一個值
- f接收yield語句的返回值
- 下面增加一個異常,發現不能保證exit執行,解決辦法:增加try finally。
- 這樣做得意義是,當yield發生處為生成器函數增加了上下文管理
總結:如果業務邏輯簡單可以使用函數加裝飾器方式,如果業務復雜,用類的方式加__enter__和__exit__
反射
- 概述:
- 運行時,區別于編譯時,指的是程序被加載到內容中執行的時候
- 反射,reflection,指的是運行時獲取類型定義信息
- 一個對象能夠在運行時,像照鏡子一樣,反射出其類型信息
- 簡單說,在python中,能夠通過一個對象,找出其type、class、attribute或method的能力,成為反射或者自省
- 反射相關函數和方法
- 需求:有一個Point類,查看它實例的屬性,并修改它,動態為實例加屬性
- 上例通過屬性字典__dict__來訪問對象的屬性,本質上也是利用的反射能力
- python提供了內置的函數
內置函數
|
意義
|
getattr(object,name[,default])
|
通過name返回object的屬性值,當屬性不存在,將使用defalut返回,如果沒有default,則拋出AttributeError。name必須為字符串
|
setattr(object,name,value)
|
object的屬性存在,則覆蓋,不存在,新增
|
hasattr(object,name)
|
判斷對象是否有這個名字的屬性,name必須為字符串
|
- 思考:這種動態增加屬性的方式和裝飾器修飾一個類、Mixin方式的差異?
- 這種動態增刪屬性的方式是運行時改變類或者實例的方式,但是裝飾器或者Mixin都是定義時就決定了,因此反射能力具有更大的靈活性
- 練習
- 1.命令分發器,通過名稱找對應的函數執行。思路:名稱找對象的方法
- 方法二中,使用getattr方法找到對象的屬性的方式,比自己維護一個字典來建立名稱和函數之間的關系好得多
反射相關的魔術方法
- __getattr__()、__setattr__()、__delattr__(),三個魔術方法
- __getattr__()測試:
一個類的屬性會按照繼承關系找,如果找不到,就會執行getattr方法,如果沒有這個方法,就會拋出AttributeError異常表示找不到屬性查找屬性順序為:instance.dict—>instance.class.dict—>繼承的祖先類(直到object)的dict—>調用getattr()
- __setattr__()舉例:
實例通過點設置屬性,如同self.x = x,就會調用__setattr__(),屬性要加到實例的dict中,就需要自己完成setattr方法,可以攔截實例屬性的添加、修改操作,如果要設置生效,需要自己操作實例的dict,此例子中需要再setattr函數中加入self.__dict__[key] = value
- __delattr__()舉例:
可以阻止通過實例刪除屬性的操作。但是通過類依然可以刪除屬性。如同Z
- __getattribute__舉例:
- 實例的所有的屬性訪問,第一個都會調用getattribute方法,它阻止了屬性的查找,該方法應該返回(計算后的)值或者拋出一個AttributeError異常
- 他的return值將作為屬性查找的結果,如果拋出AttributError異常,則會直接調用getattr方法,因為表示屬性沒有找到
- getattribute方法中為了避免在該方法中無限遞歸,他的實現應該永遠調用基類的同名方法以訪問需要的任何屬性,例如object.getattribute(self,name).
- 注意:除非你明確的知道getattribute方法用來做什么,否則不要使用它
總結:
魔術方法
|
意義
|
__getattr__()
|
通過搜索實例、實例的類及祖先類查不到屬性,就會調用此方法
|
__setattr__()
|
通過.訪問實例屬性,進行增加、修改都要調用它
|
__delattr__()
|
當通過實例來刪除屬性時調用此方法
|
__getattribute__()
|
實例所有的屬性調用都從這個方法開始
|
- 屬性查找順序
- 實例調用__getattribute__()–>instance.__dict__–>instance.__class__.__dict__–>繼承祖先類(直到object)的__dict__–>調用__getattr__()
本文來自投稿,不代表Linux運維部落立場,如若轉載,請注明出處:http://www.www58058.com/88799