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 = valueclass PythonSite(object): webframework = WebFramework()In [1]: PythonSite.webframeworkOut[1]: 'Flask'In [2]: PythonSite.webframework = 'Tornado'In [3]: PythonSite.webframeworkOut[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 valueclass Foo(object): @cached_property def foo(self): print 'first calculate' result = 'this is result' return resultf = Foo()print f.foo # first calculate this is resultprint f.foo # this is result
運行結果可見,first calculate只在第一次調用時候被計算之後就把結果緩衝起來了。這樣的好處是在網路編程中,對HTTP協議的解析,通常會把HTTP的header解析成python的一個字典,而在視圖函數的時候,可能不知一次的訪問這個header,因此把這個header使用描述器緩衝起來,可以減少多餘的解析。
描述器在python的應用十分廣泛,通常是配合裝飾器一起使用。強大的魔法來自強大的責任。描述器還可以用來實現ORM中對sql語句的"先行編譯"。恰當的使用描述器,可以讓自己的Python代碼更優雅。