1. 程式人生 > >python descriptor 官方文件指引[翻譯]

python descriptor 官方文件指引[翻譯]

翻譯自官方文件描述符指引

摘要

定義描述符,概述描述符的協議,並且展示描述符如何被呼叫。檢查自定義描述符和幾個內建的python描述符(包括函式、屬性,靜態方法和類方法)。通過一段python示例程式去展示描述符是怎麼執行的。

學習描述符不僅提供了接觸更多工具的機會,也會對python執行機制和設計優雅的程式碼有更深入的理解。

定義和介紹

一般來說,一個描述符是一個繫結的物件屬性,它能夠被描述符裡的方法覆蓋。這樣的方法有__get__(), __set__() 和__delete__()。如果有一個物件定義了這些方法,那就可以稱作一個描述符。

訪問屬性的預設行為是從一個物件的字典中get,set或delete這個屬性。

例如,a.x有一個以a.__dict__['x']開頭的查詢鏈,然後是type(a).__dict__['x'],最後是除了元類的type(a) 的基類。如果查詢的值是描述符方法定義的物件,那麼python就會重寫預設的行為,並且呼叫描述符的方法。在優先順序鏈中發生的位置取決於定義了哪些描述符方法。注意,描述符僅僅被呼叫用於新式物件或類(類繼承自object或者type)。

描述符是一個強大的通用協議。它們是屬性,方法,靜態方法,類方法和super()背後的機制。 它們被用於整個Python本身以實現在2.2版本中引入的新風格類。描述符簡化了底層C程式碼,為日常Python程式提供了一套靈活的新工具。

描述符協議

descr.__get__(self, obj, type=None) --> value

descr.__set__(self, obj, value) --> None

descr.__delete__(self, obj) --> None

以上就是所有的描述符協議,一個物件定義了任何以上的方法都可以視為描述符,並且可以在查詢屬性時重寫預設的行為。

如果一個物件定義了__get()__和__set__(),這個物件就可視為一個數據描述符。如果只定義了__get__()就被稱為非資料描述符。

資料和非資料描述符的區別在於如何覆蓋對於例項的字典中的條目計算。如果例項的字典具有與資料描述符相同名稱的條目,則資料描述符優先。如果例項的字典具有與非資料描述符相同名稱的條目,則字典條目優先。


實現一個只讀的資料描述符,需要用到__get__()和__set__(),當這個描述符被呼叫的時候__set__()丟擲一個AttributeError,用一個丟擲異常的佔位去定義__set__()方法足以使之成為資料描述符。

獲取描述符

一個描述符可以被方法名直接呼叫,例如,d.__get__(obj).

另外一種更常見的是在訪問屬性的時候自動呼叫描述符。例如,obj.d是在obj的字典中查詢d。如果d定義了__get__()方法,則根據下面列出的優先順序規則呼叫d .__get__(obj)。

呼叫的細節取決於obj是一個物件還是一個類。無論哪種方式,描述符只適用於新樣式物件和類。新式類是他的子類繼承於object。

對於物件,這構造是在object.__getattribute__()中轉換b.x為type(b).__dict__['x'].__get__(b, type(b))。該實現通過一個數據描述符>例項變數>非資料描述符的優先鏈,並且如果__getattr__()存在,就賦予它最低優先順序,完整的c語言實現可以在object.c中找到。

對於類,這構造是在type.__getattribute__()中轉換B.x為B.__dict__['x'].__get__(b, type(b))。用python實現如下:

def __getattribute__(self, key):
    "Emulate type_getattro() in Objects/typeobject.c"
    v = object.__getattribute__(self, key)
    if hasattr(v, '__get__'):
        return v.__get__(None, self)
    return v
(佔坑更新。。。)