python描述子descriptor(一)

黄舟
發布: 2016-12-23 17:37:18
原創
1176 人瀏覽過

Python 描述符是一種建立託管屬性的方法。每當一個屬性被查詢時,就會發生一個動作。這個動作預設是get,set或delete。不過,有時候某個應用可能會有

更多的需求,需要你設計一些更複雜的動作。最好的解決方案就是編寫一個執行符合需求的動作的函數,然後指定它在屬性被存取時運行。一個具有這種功能的物件

稱為描述符。描述子是python方法,綁定方法,super,PRoperty,staticmethod和classmethod的實作基礎。

1.描述子協定

描述子descriptor就是一個表示屬性值的對象,透過實作一個或多個__get__,__set__,__delete__方法,可以將描述子與屬性存取機制掛鉤,也可以自訂這些操作。

__get__(self,instance,own):用於存取屬性,傳回屬性的值。 instance為使用描述符的實例對象,own為實例所屬的類別。當透過類別存取屬性時,instance為None。

__set__(self,instance,value):設定屬性值。

__delete__(self,instance):刪除屬性值。

2.描述子如何實作

class Descriptor(object):
   def __get__(self, instance, owner):
      print 'getting:%s'%self._name  def __set__(self, instance, name):
       print 'setting:%s'%name
       self._name = name
   def __delete (self, instance): name
   def __delete____________f, instance): name
   def __delete____________f, instance): %%):com     del self._name
class Person (object):
   name = Descriptor()


一個很簡單的描述符物件就產生了,現在可以對一個Person物件進行屬性name的讀取,設定與刪除:

>>> p=Person( )

>>> p.name='john'

setting:john
>>> p.name
getting:john
'john'
>>> del p.name
deleting:john

>>> del p.name
deleting:john

注意:描述詞只能在類別層級上進行實例化,不能透過在__init__()和其他方法中建立描述符物件來為每個實例建立描述符。

具有描述符的類別使用的屬性名稱比實例上儲存的屬性名稱具有更高的優先權。為了能讓描述符在實例上儲存值,描述符必須挑選一個與它本身所用名稱不同的名稱。

如上例,Person類別初始化__init__函數為實例設定屬性就不能用name名稱了。

data描述符與none-data描述符:

如果實作了__get__和__set__就是一個data描述符,如果只有__get__就是一個non-data描述符。不同的效果在於data描述符總是替代在一個實例中的屬性實現,

而non-data描述符由於沒有set,在通過實例對屬性賦值時,例如上面的p.name = 'hello',不會再呼叫__set__方法,會直接把實例屬性p.name設為'hello'。

當然如果僅僅在__set__中raise AttributeError,仍然得到的是一個non-data的描述符。

描述符呼叫機制:

當查詢一個物件的屬性a.attr時,如果python發現attr是個描述子對象,如何讀取屬性取決於物件a:

直接呼叫:最簡單的呼叫是直接使用程式碼呼叫描述符的方法,attr.__get__(a)

實例綁定:如果a是個實例對象,呼叫方法:type(a).__dict__['attr'].__get__(a,type(a))

類別綁定:如果A是個類別對象,呼叫方法:A.__dict__['attr'].__get__(None,A)

super綁定:如果a是個super實例,那麼super(B,obj).m ()透過查詢obj.__class__.__mro__找到B的基類A,然後執行A.__dict__['m'].__get__(obj,obj.__class__)

3.執行屬性類型檢查的描述符🎜

class TypedProperty(object):
   def __init__(self,name,attr_type,default=None):
       self.name='_'+name
       遠default if default else attr_type()
   def __get__(self,instance,own):
       return getattr(instance,self.name,self.default)
   
           raise TypeError,'Must be %s'%self.type
       setattr(instance,self.name,value)
   def __delete__(self,instance):
     def __delete__(self,instance):
    class 集ject):
   name=TypedProperty('name',str)
   num=TypedProperty('num',int,37)                
,str設為int型,就會報錯:

>>> f.name=21
TypeError: Must be

且禁止對屬性進行刪除操作:


>>> del f.name
AttributeError: Can not delete attribute

f.name 隱形的呼叫type(f).__dict__['name'].__get__(f,Foo),即Foo.name.__get__(f,Foo)。


上述描述符實際上是儲存在實例上的,name透過setattr(f,_name,value)儲存在f._name上,num儲存在f._num上,這也是加底線的原因,

否則描述符名稱name會和實例屬性name發生衝突,描述符屬性f.name會覆寫實例屬性f.name。

 以上就是python描述符descriptor(一)的內容,更多相關內容請關注PHP中文網(www.php.cn)!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板