python裝飾器和描述器的使用總結
被某些中文教程坑過,我的建議是有問題看官方文檔,即使沒有很詳細的例子,至少不坑
裝飾器
毫無疑問在python中用得非常多
def deco(func):
def _deco():
print 'before invoked'
func()
print 'after invoked'
return _deco
@deco
def f():
print 'f is invoked' 在f上加deco裝飾器相當于f = deco(f), 和functools.partial有些類似
如果被裝飾的函數f帶參數且有返回值
def deco(func):
def _deco(*args, **kwargs):
print 'before invoked'
ret = func(*args, **kwargs)
print 'after invoded'
return ret
return _deco
@deco
def f(a):
print 'f is invoked'
return a + 1 如果裝飾器帶有參數,需要多包一層,把參數調用包進去
def deco(*args):
def _deco(func):
def __deco(*args, **kwargs):
print 'decorator args is', args
print 'before invoked'
ret = func(*args, **kwargs)
print 'after invoded'
return ret
return __deco
return _deco
@deco('test')
def f(a):
print 'f is invoked'
return a + 1 只有最里面一層的__deco才會每次都調用,其它外層函數只在包裝時調用一次,當然,你可以在其中聲明變量,然后拿到__deco里使用。如果需要保留函數名,則在__deco上加@functools.wraps裝飾器
使用 類 作裝飾器,注意是類此時相當于裝飾函數,被裝飾的函數會作為實例化參數,得到一個類實例,以python wiki上一個做登錄檢查的代碼為例
class LoginCheck:
def __init__(self, f):
self._f = f
def __call__(self, *args):
Status = check_function()
if Status is 1:
return self._f(*args)
else:
return alt_function()
def check_function():
return test
def alt_function():
return 'Sorry - this is the forced behaviour'
@LoginCheck
def display_members_page():
print 'This is the members page' 描述器
描述器在監視特定屬性的時候很有用,其只在新式類中起作用。所有的描述器協議如下:
descr.__get__(self, obj, type=None) --> value
descr.__set__(self, obj, value) --> None
descr.__delete__(self, obj) --> None
如果一個對象同時定義了 __get__() 和 __set__(),它叫做資料描述器(data descriptor)。僅定義了 __get__() 的描述器叫非資料描述器
描述器在屬性訪問時被自動調用。舉例來說, obj.x 會在 obj 的字典中找x ,如果x定義了 __get__方法,那么 x.__get__(obj)會依據下面的優先規則被調用
調用優先級:
資料描述器 -> 實例字典 -> 非資料描述器
常用的描述器就是property了,一般都只實現了__get__的接口
先給出一個classmethod的實現和一個用于測試描述器優先級的類
class classproperty(object):
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
return self.func(owner)
class MyClass(object):
@classproperty
def name(cls):
return cls.__name__
@property
def x(self):
return self._data
@x.setter
def x(self, value):
self._data = value
@x.deleter
def x(self):
del self._data
def __init__(self, val):
self._data = val
self.x = 3
self.name = 'test' 接下來調用
s = MyClass(99) print s.x print s.name print s.__dict__
很明顯x是資料描述器,而name不是,所以結果是
3
5
{'_data': 3, 'name': 'test'}
如果給classproperty加上__set__,那么就會調用被裝飾的name,而不是實例化時實例字典中的name
一個property的python 實現
class Property(object):
"Emulate PyProperty_Type() in Objects/descrobject.c"
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
self.__doc__ = doc
def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.fget is None:
raise AttributeError, "unreadable attribute"
return self.fget(obj)
def __set__(self, obj, value):
if self.fset is None:
raise AttributeError, "can't set attribute"
self.fset(obj, value)
def __delete__(self, obj):
if self.fdel is None:
raise AttributeError, "can't delete attribute"
self.fdel(obj)
def getter(self, fget):
return type(self)(fget, self.fset, self.fdel, self.__doc__)
def setter(self, fset):
return type(self)(self.fget, fset, self.fdel, self.__doc__)
def deleter(self, fdel):
return type(self)(self.fget, self.fset, fdel, self.__doc__)