欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入解析Python中的descriptor描述器的作用及用法

 更新時間:2016年06月27日 16:02:52   作者:cangmean  
在Python中描述器也被稱為描述符,描述器能夠?qū)崿F(xiàn)對對象屬性的訪問控制,下面我們就來深入解析Python中的descriptor描述器的作用及用法

一般來說,一個描述器是一個有“綁定行為”的對象屬性(object attribute),它的訪問控制被描述器協(xié)議方法重寫。這些方法是 __get__(), __set__(), 和 __delete__() 。有這些方法的對象叫做描述器。

默認對屬性的訪問控制是從對象的字典里面(__dict__)中獲取(get), 設(shè)置(set)和刪除(delete)它。舉例來說, a.x 的查找順序是, a.__dict__['x'] , 然后 type(a).__dict__['x'] , 然后找 type(a) 的父類(不包括元類(metaclass)).如果查找到的值是一個描述器, Python就會調(diào)用描述器的方法來重寫默認的控制行為。這個重寫發(fā)生在這個查找環(huán)節(jié)的哪里取決于定義了哪個描述器方法。注意, 只有在新式類中時描述器才會起作用。(新式類是繼承自 type 或者 object 的類)

描述器是強大的,應(yīng)用廣泛的。描述器正是屬性, 實例方法, 靜態(tài)方法, 類方法和 super 的背后的實現(xiàn)機制。描述器在Python自身中廣泛使用,以實現(xiàn)Python 2.2中引入的新式類。描述器簡化了底層的C代碼,并為Python的日常編程提供了一套靈活的新工具。

描述器協(xié)議

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

一個對象如果是一個描述器,被當做對象屬性(很重要)時重寫默認的查找行為。

如果一個對象同時定義了__get__和__set__,它叫data descriptor。僅定義了__get__的描述器叫non-data descriptor。

data descriptor和non-data descriptor區(qū)別在于: 相對于實例的字典的優(yōu)先級,如果實例字典有與描述器具同名的屬性,如果描述器是data descriptor,優(yōu)先使用data descriptor。如果是non-data descriptor,優(yōu)先使用字典中的屬性。

class B(object):

  def __init__(self):
    self.name = 'mink'

  def __get__(self, obj, objtype=None):
    return self.name

class A(object):
  name = B()

a = A()
print a.__dict__  # print {}
print a.name    # print mink
a.name = 'kk'    
print a.__dict__  # print {'name': 'kk'}
print a.name    # print kk

這里B是一個non-data descriptor所以當a.name = 'kk'的時候,a.__dict__里會有name屬性, 接下來給它設(shè)置__set__

def __set__(self, obj, value):
  self.name = value

 ... do something

a = A()
print a.__dict__  # print {}
print a.name    # print mink
a.name = 'kk'    
print a.__dict__  # print {}
print a.name    # print kk

因為data descriptor訪問屬性優(yōu)先級比實例的字典高,所以a.__dict__是空的。

描述器的調(diào)用
描述器可以直接這么調(diào)用: d.__get__(obj)

然而更常見的情況是描述器在屬性訪問時被自動調(diào)用。舉例來說, obj.d 會在 obj 的字典中找 d ,如果 d 定義了 __get__ 方法,那么 d.__get__(obj) 會依據(jù)下面的優(yōu)先規(guī)則被調(diào)用。

調(diào)用的細節(jié)取決于 obj 是一個類還是一個實例。另外,描述器只對于新式對象和新式類才起作用。繼承于 object 的類叫做新式類。

對于對象來講,方法 object.__getattribute__() 把 b.x 變成 type(b).__dict__['x'].__get__(b, type(b)) 。具體實現(xiàn)是依據(jù)這樣的優(yōu)先順序:資料描述器優(yōu)先于實例變量,實例變量優(yōu)先于非資料描述器,__getattr__()方法(如果對象中包含的話)具有最低的優(yōu)先級。完整的C語言實現(xiàn)可以在 Objects/object.c 中 PyObject_GenericGetAttr() 查看。

對于類來講,方法 type.__getattribute__() 把 B.x 變成 B.__dict__['x'].__get__(None, 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

其中重要的幾點:

  • 描述器的調(diào)用是因為 __getattribute__()
  • 重寫 __getattribute__() 方法會阻止正常的描述器調(diào)用
  • __getattribute__() 只對新式類的實例可用
  • object.__getattribute__() 和 type.__getattribute__() 對 __get__() 的調(diào)用不一樣
  • 資料描述器總是比實例字典優(yōu)先。
  • 非資料描述器可能被實例字典重寫。(非資料描述器不如實例字典優(yōu)先)
  • super() 返回的對象同樣有一個定制的 __getattribute__() 方法用來調(diào)用描述器。調(diào)用 super(B, obj).m() 時會先在 obj.__class__.__mro__ 中查找與B緊鄰的基類A,然后返回 A.__dict__['m'].__get__(obj, A) 。如果不是描述器,原樣返回 m 。如果實例字典中找不到 m ,會回溯繼續(xù)調(diào)用 object.__getattribute__() 查找。(譯者注:即在 __mro__ 中的下一個基類中查找)

注意:在Python 2.2中,如果 m 是一個描述器, super(B, obj).m() 只會調(diào)用方法 __get__() 。在Python 2.3中,非資料描述器(除非是個舊式類)也會被調(diào)用。 super_getattro() 的實現(xiàn)細節(jié)在: Objects/typeobject.c ,[del] 一個等價的Python實現(xiàn)在 Guido's Tutorial [/del] (譯者注:原文此句已刪除,保留供大家參考)。

以上展示了描述器的機理是在 object, type, 和 super 的 __getattribute__() 方法中實現(xiàn)的。由 object 派生出的類自動的繼承這個機理,或者它們有個有類似機理的元類。同樣,可以重寫類的 __getattribute__() 方法來關(guān)閉這個類的描述器行為。

描述器例子
下面的代碼中定義了一個資料描述器,每次 get 和 set 都會打印一條消息。重寫 __getattribute__() 是另一個可以使所有屬性擁有這個行為的方法。但是,描述器在監(jiān)視特定屬性的時候是很有用的。

class RevealAccess(object):
  """A data descriptor that sets and returns values
    normally and prints a message logging their access.
  """

  def __init__(self, initval=None, name='var'):
    self.val = initval
    self.name = name

  def __get__(self, obj, objtype):
    print 'Retrieving', self.name
    return self.val

  def __set__(self, obj, val):
    print 'Updating' , self.name
    self.val = val

>>> class MyClass(object):
  x = RevealAccess(10, 'var "x"')
  y = 5

>>> m = MyClass()
>>> m.x
Retrieving var "x"
10
>>> m.x = 20
Updating var "x"
>>> m.x
Retrieving var "x"
20
>>> m.y
5

這個協(xié)議非常簡單,并且提供了令人激動的可能。一些用途實在是太普遍以致于它們被打包成獨立的函數(shù)。像屬性(property), 方法(bound和unbound method), 靜態(tài)方法和類方法都是基于描述器協(xié)議的。

相關(guān)文章

  • Python使用enumerate獲取迭代元素下標

    Python使用enumerate獲取迭代元素下標

    這篇文章主要介紹了python使用enumerate獲取迭代元素下標,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-02-02
  • Python使用Pyqt5實現(xiàn)簡易瀏覽器(最新版本測試過)

    Python使用Pyqt5實現(xiàn)簡易瀏覽器(最新版本測試過)

    這篇文章主要介紹了Python使用Pyqt5實現(xiàn)簡易瀏覽器(最新版本測試過),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-04-04
  • 如何將自己寫的模塊上傳到pypi

    如何將自己寫的模塊上傳到pypi

    這篇文章主要介紹了如何將自己寫的模塊上傳到pypi,幫助大家更好的理解和學習使用python,感興趣的朋友可以了解下
    2021-03-03
  • Python的三種主要模塊介紹

    Python的三種主要模塊介紹

    這篇文章介紹了Python的三類主要模塊,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-07-07
  • pytorch 中autograd.grad()函數(shù)的用法說明

    pytorch 中autograd.grad()函數(shù)的用法說明

    這篇文章主要介紹了pytorch 中autograd.grad()函數(shù)的用法說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05
  • Python3.4 splinter(模擬填寫表單)使用方法

    Python3.4 splinter(模擬填寫表單)使用方法

    今天小編就為大家分享一篇Python3.4 splinter(模擬填寫表單)使用方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-10-10
  • python可變對象,不可變對象詳解

    python可變對象,不可變對象詳解

    這篇文章主要介紹了Python可變對象和不可變對象的相關(guān)資料,文中講解非常細致,代碼幫助大家更好的理解和學習,感興趣的朋友可以了解下
    2021-09-09
  • Python 如何定義匿名或內(nèi)聯(lián)函數(shù)

    Python 如何定義匿名或內(nèi)聯(lián)函數(shù)

    這篇文章主要介紹了Python 如何定義匿名或內(nèi)聯(lián)函數(shù),文中講解非常細致,代碼幫助大家更好的理解和學習,感興趣的朋友可以了解下
    2020-08-08
  • python清除字符串里非字母字符的方法

    python清除字符串里非字母字符的方法

    這篇文章主要介紹了python清除字符串里非字母字符的方法,涉及Python字符串正則替換操作的相關(guān)技巧,需要的朋友可以參考下
    2015-07-07
  • python讀取文件由于編碼問題失敗匯總以及解決辦法

    python讀取文件由于編碼問題失敗匯總以及解決辦法

    這篇文章主要給大家介紹了關(guān)于python讀取文件由于編碼問題失敗匯總以及解決辦法的相關(guān)資料,文件編碼錯誤指的是在Python讀取文件的過程中出現(xiàn)的編碼不匹配的問題,需要的朋友可以參考下
    2023-10-10

最新評論