被某些中文教程坑過,我的建議是有問題看官方文件,即使沒有很詳細的例子,至少不坑
裝飾器
毫無疑問在python中用得非常多
1 2 3 4 5 6 7 8 9 10 |
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
帶引數且有返回值
1 2 3 4 5 6 7 8 9 10 11 12 |
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 |
如果裝飾器帶有引數,需要多包一層,把引數呼叫包進去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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上一個做登入檢查的程式碼為例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
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
的實現和一個用於測試描述器優先順序的類
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
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' |
接下來呼叫
1 2 3 4 |
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 實現
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
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__) |