首頁  >  文章  >  後端開發  >  詳解Python中的descriptor描述器簡潔使用指南

詳解Python中的descriptor描述器簡潔使用指南

高洛峰
高洛峰原創
2017-03-03 15:53:381374瀏覽

descriptor在Python中主要被用來定義方法和屬性,使用起來相當具有技巧性,這裡我們先從基礎的開始,整理一份Python中的descriptor描述器簡明使用指南

#當定義迭代器的時候,描述是實現迭代協議的對象,也就是實現__iter__方法的對象。同理,所謂描述器,即實現了描述符協議,即__get__, __set__, 和 __delete__方法的對象。

單看定義,還是比較抽象的。 talk is cheap。看程式碼:

class WebFramework(object):
  def __init__(self, name='Flask'):
    self.name = name

  def __get__(self, instance, owner):
    return self.name

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


class PythonSite(object):

  webframework = WebFramework()

In [1]: PythonSite.webframework
Out[1]: 'Flask'

In [2]: PythonSite.webframework = 'Tornado'

In [3]: PythonSite.webframework
Out[3]: 'Tornado'

定義了一個類別WebFramework,它實作了描述子協定__get__和__set__,該物件(類別也是對象,一切都是物件)即成為了一個描述器。同時實作__get__和__set__的稱為資料描述器(data descriptor)。僅實作__get__的則為非描述器。兩者的差異是相對於實例的字典的優先順序。

如果實例字典中有與描述器同名的屬性,如果描述器是資料描述器,優先使用資料描述器,如果是非資料描述器,優先使用字典中的屬性。

描述器的呼叫
對於這類魔法,其呼叫方法往往不是直接使用的。例如裝飾器需要用 @ 符號呼叫。迭代器通常在迭代過程,或使用 next 方法呼叫。描述器則比較簡單,物件屬性的時候會呼叫。

In [15]: webframework = WebFramework()

In [16]: webframework.__get__(webframework, WebFramework)
Out[16]: 'Flask'

#描述器的應用程式
描述器的作用主要在方法和屬性的定義上。既然我們可以重新描述類別的屬性,那麼這個魔法就可以改變類別的一些行為。最簡單的應用則是可以配合裝飾器,寫出一個類別屬性的快取。 Flask的作者寫了一個werkzeug網路工具庫,裡面就使用描述器的特性,實作了一個快取器。

class _Missing(object):
  def __repr__(self):
    return 'no value'

  def __reduce__(self):
    return '_missing'


_missing = _Missing()


class cached_property(object):
  def __init__(self, func, name=None, doc=None):
    self.__name__ = name or func.__name__
    self.__module__ = func.__module__
    self.__doc__ = doc or func.__doc__
    self.func = func

  def __get__(self, obj, type=None):
    if obj is None:
      return self
    value = obj.__dict__.get(self.__name__, _missing)
    if value is _missing:
      value = self.func(obj)
      obj.__dict__[self.__name__] = value
    return value


class Foo(object):
  @cached_property
  def foo(self):
    print 'first calculate'
    result = 'this is result'
    return result


f = Foo()

print f.foo  # first calculate this is result
print f.foo  # this is result

運行結果可見,first calculate只在第一次呼叫時候被計算之後就把結果快取起來了。這樣的好處是在網頁程式設計中,對HTTP協定的解析,通常會把HTTP的header解析成python的一個字典,而在視圖函數的時候,可能不知一次的訪問這個header,因此把這個header使用描述器快取起來,可以減少多餘的解析。

描述器在python的應用十分廣泛,通常是配合裝飾器一起使用。強大的魔法來自強大的責任。描述器也可以用來實現ORM中對sql語句的"預編譯"。恰當的使用描述器,可以讓自己的Python程式碼更優雅。

更多詳解Python中的descriptor描述器簡潔使用指南相關文章請關注PHP中文網!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn