python的__get__、__set__、__delete__(1)
內(nèi)容:
??? 描述符引導
??????? 摘要
??????? 定義和介紹
??????? 描述符協(xié)議
??????? 調(diào)用描述符
??????? 樣例
??????? Properties
??????? 函數(shù)和方法
??????? 靜態(tài)方法和類方法
?????? ?
?? ?
摘要
??? 定義并展示如何調(diào)用描述符,展示自定義描述符和幾個內(nèi)置的python描述符,包括函數(shù)、屬性、靜態(tài)方法和類方法,通過給出一個Python的示例應用來展示描述符是如何工作的.
??? 熟練掌握描述符不僅讓你擁有python使用的額外技巧,并且可以加深對Python內(nèi)部如何工作的理解,提升對程序設計的能力,而且體會到python的設計優(yōu)雅之處
定義和介紹
??? 一般來說,描述符是帶有“綁定行為”的對象屬性,它的屬性訪問已經(jīng)被描述符協(xié)議中的方法覆蓋了.這些方法是__get__(),__set__(),和__delete__().
??? 如果一個對象定義了這些方法中的任何一個,它就是一個描述符.
??? 默認的屬相訪問是從對象的字典中 get, set, 或者 delete 屬性,;例如a.x的查找順序是:
??? a.x -> a.__dict__['x'] -> type(a).__dict__['x'] -> type(a)的基類(不包括元類),如果查找的值是對象定義的描述方法之一,python可能會調(diào)用描述符方法來重載默認行為,
??? 發(fā)生在這個查找環(huán)節(jié)的哪里取決于定義了哪些描述符方法
??? 注意,只有在新式類中描述符才會起作用(新式類繼承type或者object class)
??? 描述符是強有力的通用協(xié)議,屬性、方法、靜態(tài)方法、類方法和super()背后使用的就是這個機制,描述符簡化了底層的c代碼,并為Python編程提供了一組靈活的新工具
描述符協(xié)議
??? 定義任何上面三個方法的任意一個,這個對象就會被認為是一個描述符,并且可以在被作為對象屬性時重載默認的行為, 如果一個對象定義了__get__() 和 __set__(),它被認為是一個數(shù)據(jù)描述符.只定義 __get__()被認為是非數(shù)據(jù)描述符,數(shù)據(jù)和非數(shù)據(jù)描述符的區(qū)別在于:如果一個實例的字典有和數(shù)據(jù)描述符同名的屬性,那么數(shù)據(jù)描述符會被優(yōu)先使用,如果一個實例的字典實現(xiàn)了無數(shù)據(jù)描述符的定義,那么這個字典中的屬性會被優(yōu)先使用,實現(xiàn)只讀數(shù)據(jù)描述符,同時定義__get__()和__set__(),在__set__()中拋出AttributeError.
描述符調(diào)用
??? 描述符可以直接用方法名稱調(diào)用,比如:d.__get__(obj)
?? ?
??? 然而,描述符更常用的方式是屬性訪問時被自動調(diào)用,例如:obj.d 在obj的字典中查找d,如果d定義了方法__get__(),然后d.__get__(obj)會被通過下面的優(yōu)先級列表調(diào)用
??? 詳細的調(diào)用依賴于obj是一個對象還是一個類,不管哪種方式,描述符只工作在新式對象和類,如果一個類是object的子類(繼承object),這個類就是一個新式類
?? ?
??? 對于對象來說,object.__getattribute__() 把b.x 變?yōu)?type(b).__dict__['x'].__get__(b, type(b)) .優(yōu)先級順序:
??? 數(shù)據(jù)描述符 > 實例變量 > 非數(shù)據(jù)描述符,__getattr__()具有最低優(yōu)先級(如果實現(xiàn)了的話),C語言的實現(xiàn)可以在 Objects/object.c 中 PyObject_GenericGetAttr() 查看.
?? ?
??? 對于類來說,type.__getattribute__() 把 B.x 變?yōu)?B.__dict__['x'].__get__(None, B),代碼實現(xiàn)為:
?? ?
??? 重點:
??????? 描述符被__getattribute()方法調(diào)用
??????? 重載__getattribute__()會阻止描述符自動調(diào)用
??????? __getattribute__()只適用于新式類和對象
??????? object.__getattribute__()和type.__getattribute__()對__get__()的調(diào)用不一樣
??????? 數(shù)據(jù)描述符會重載實例字典
??????? 非數(shù)據(jù)描述符可能會被實例字典重載
?????? ?
???? super()返回的對象會使用定制__getattribute__()方法來調(diào)用描述符,調(diào)用super(B, obj).m() 會在緊鄰著B的基類A搜索obj.__class__.__mro__然后返回A.__dict__['m'].__get__(obj, B),如果不是一個描述符,返回未改變的m
???? 如果不在字典中,m會調(diào)用 object.__getattribute__() 查詢
???? 注意:在python2.2,如果m是一個數(shù)據(jù)描述符,super(B, obj).m() 會調(diào)用__get__(),在python2.3,無數(shù)據(jù)描述符也會執(zhí)行調(diào)用,除非是個舊式類,super_getattro() 的細節(jié)在Objects/typeobject.c中
??? ?
???? 上面展示的是描述符在object, type, and super() 的 __getattribute__() 方法中的實現(xiàn)機制,繼承object的類自動實現(xiàn)或者他們有一個元類提供類似的功能,同樣,重載 __getattribute__()可以停止描述符的調(diào)用
??? ?
描述符例子
??? 下面的代碼創(chuàng)建了一個類,每次訪問get或者set都會打印一條信息.重載__getattribute__()也可以使每個屬性實現(xiàn)這一方法,然而,描述符在查看特定的屬性時比較有用
??? 這個協(xié)議很簡單卻又可以提供令人為之一振的可能性.Properties, bound 和 unbound methods, 靜態(tài)方法和 類方法 都是基于描述符協(xié)議
Properties
??? 調(diào)用property()是一種建立數(shù)據(jù)描述符的方便方法,可以在訪問一個屬性的時候觸發(fā)方法的調(diào)用
??? property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
??? 下面展示一個定義管理屬性x的典型的樣例:
??? property()使用純python方式實現(xiàn)描述符:
class Property(object):"Emulate PyProperty_Type() in Objects/descrobject.c"def __init__(self, fget=None, fset=None, fdel=None, doc=None):self.fget = fgetself.fset = fsetself.fdel = fdelif doc is None and fget is not None:doc = fget.__doc__self.__doc__ = docdef __get__(self, obj, objtype=None):if obj is None:return selfif self.fget is None:raise AttributeError("unreadable attribute")return self.fget(obj)def __set__(self, obj, value):if self.fset is None:raise AttributeError("can't set attribute")self.fset(obj, value)def __delete__(self, obj):if self.fdel is None:raise AttributeError("can't delete attribute")self.fdel(obj)def getter(self, fget):return type(self)(fget, self.fset, self.fdel, self.__doc__)def setter(self, fset):return type(self)(self.fget, fset, self.fdel, self.__doc__)def deleter(self, fdel):return type(self)(self.fget, self.fset, fdel, self.__doc__)?
??? 當用戶接口已經(jīng)授權訪問屬性,這時候需求發(fā)生變化,property()可以提供便利, 例如,一個電子表格類可以通過單元('b10')授予對單元格值的訪問權.這時候,對程序的后續(xù)改進要求在每次訪問時重新計算單元格的值;然而,程序員不希望影響現(xiàn)有客戶端代碼.解決方案是在屬性數(shù)據(jù)描述符中封裝對value屬性的訪問:
函數(shù)和方法
??? python的面向對象是建立在函數(shù)的基礎上,使用非數(shù)據(jù)描述符,兩者會結合的非常緊密.
??? 類的字典將方法比作函數(shù)存儲.在一個類的定義中,使用def和lambda來聲明方法,這是用于創(chuàng)建函數(shù)的常用工具. 唯一不同之處,就是第一個參數(shù)用來表示對象實例,python約定,實例引用可以使self或者this或者其他變量名稱
??? 為了支持方法調(diào)用,函數(shù)通過__get__()方法來實現(xiàn)屬性訪問時的方法綁定
??? 這說明所有的函數(shù)都是非數(shù)據(jù)描述符,它返回綁定或者非綁定方法依賴于它被對象還是類調(diào)用
?? ?
??? 在python中的實現(xiàn)如下:
??? 在解釋器中展示函數(shù)描述符如何運行:
??? 輸出說明綁定和未綁定方法是兩種不同類型,PyMethod_Type在 Objects/classobject.c 中實際的C實現(xiàn)是一個具有有兩種不同表現(xiàn)形式的單一對象,依賴于im_self是set還是null(等價C中的None)
??? 同樣,調(diào)用方法對象的效果依賴于im_self,如果set(綁定),原函數(shù)(存儲在im_func中)被調(diào)用,它的第一個參數(shù)設置為實例.
??? 如果unbound,所有的參數(shù)不做改變的傳給原函數(shù),instancemethod_call()的C實現(xiàn)因為包含一些類型檢查會復雜一些
靜態(tài)方法和類方法
??? 無數(shù)據(jù)描述符提供一種簡單的機制將函數(shù)綁定為方法
??? 簡單地說,函數(shù)的__get__()方法會將函數(shù)被作為屬性訪問時轉換為方法,非數(shù)據(jù)描述符將 obj.f(*args) 調(diào)用為f(obj, *args).調(diào)用 klass.f(*args)變?yōu)閒(*args)
??? 下面的表格匯總了綁定和它常見的兩種變化
??????? Transformation?? ?Called from an Object?? ?Called from a Class
??????? function?? ???? f(obj, *args)?? ???????? ?? f(*args)
??????? staticmethod?? f(*args)?? ??????????? f(*args)
??????? classmethod?? ???? f(type(obj), *args)?? ???????? f(klass, *args)
??? 調(diào)用c.f 或者 C.f等價于 object.__getattribute__(c, "f") 或者 object.__getattribute__(C, "f"),不管從對象還是類中,這個函數(shù)都可以訪問到
??? 不需要self變量的方法適合使用靜態(tài)方法
??? 例如:一個統(tǒng)計包可能包括用于實驗數(shù)據(jù)的容器類,該類提供了用于計算依賴于數(shù)據(jù)的平均、平均值、中值和其他描述性統(tǒng)計數(shù)據(jù)的常規(guī)方法.可是可能有一些概念相關但不依賴數(shù)據(jù)的函數(shù).
??? 例如:erf(x)是一個不依賴于特定數(shù)據(jù)集的函數(shù),它可以從一個類或者函數(shù)調(diào)用:s.erf(1.5) --> .9332 或者 Sample.erf(1.5) --> .9332
??? 靜態(tài)方法返回原始函數(shù):
??? python版本使用非數(shù)據(jù)描述符的實現(xiàn)方法:
?
??? 與靜態(tài)方法不同,類方法在調(diào)用函數(shù)之前先將類的引用預添加到參數(shù)列表中.調(diào)用者不管是對象還是類,這種格式是相同的
??? 這種行為在函數(shù)只需要有類引用且不關心任何底層數(shù)據(jù)的情況下是有用的,類方法的一個用途是用來創(chuàng)建不同的類構造器,在python2.3中,類方法dict.fromkeys()可以使用一個key的列表來創(chuàng)建字典,python的實現(xiàn)方式:
?
??? 現(xiàn)在可以這樣創(chuàng)建一個字典:
>>> Dict.fromkeys('abracadabra'){'a':None, 'r':None, 'b':None, 'c':None, 'd':None}
??? classmethod()使用無數(shù)據(jù)描述符協(xié)議實現(xiàn):
?
本文翻譯原文地址:https://docs.python.org/2/howto/descriptor.html#id9
轉載于:https://www.cnblogs.com/flashBoxer/p/9771797.html
總結
以上是生活随笔為你收集整理的python的__get__、__set__、__delete__(1)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LeetCode 819. Most C
- 下一篇: POJ2976——Dropping te