2012-05-28 37 views
1

我試圖編寫一個像@property一樣工作的裝飾器,但遇到了一些問題。在Python 2.7中定義「屬性」類裝飾器

class Dec(object): 
    def __init__(self, fn): 
    self._fn = fn 
    self._before = None 
    @property 
    def before(self) 
    return self._before 
    @before.setter 
    def before(self, fn): 
    self._before = fn 
    def __call__(self, *args, **kwargs): 
    self._before(*args, **kwargs) 
    self._fn(*args, **kwargs) 

def withbefore(fn): 
    return Dec(fn) 

它是一個簡單的鏈接裝飾器。 @property/@。setter語法正是我想要克隆的。

這工作:

@withbefore 
def foo(): 
    ... 
@foo.before 
def beforefoo(): 
    ... 

但在一個類就不會:

class Weee(object): 
    @withbefore 
    def do_stuff(self): 
     pass 
    @do_stuff.before 
    def before_do_stuff(self): 
     pass 

它提出了一個導入錯誤。如何正確模擬@property /。{setter,getter,deleter}?如何正確模擬@property /。{setter,getter,deleter}?

+0

爲什麼''之前'實際上是一個屬性? – poke

回答

2

實際上,它引發了一個TypeError。

無論如何,運行裝飾函數時也遇到同樣的錯誤。發生這種情況的原因是,當您使用@foo.before裝飾一個函數時,它將使用裝飾函數作爲參數調用self._before函數。由於self._beforeNone,它會引發錯誤。

它有各種解決方案。我最喜歡的是一個不同的默認值設置爲self._before - 一個功能,將設置self._before值:

class Dec(object): 
    def __init__(self, fn): 
    self._fn = fn 
    def setbefore(b): 
     self._before = b 
    self._before = self.default_before = setbefore 

當然,這個功能當Dec對象被稱爲不應該叫,所以我們改變__call__方法:

def __call__(self, *args, **kwargs): 
     if self._before != self.default_before: 
     self._before(*args, **kwargs) 
     self._fn(*args, **kwargs) 
+1

啊,我有你。整個@property部分我覺得非常愚蠢,沒有它就更加乾淨。此外,一旦這樣做,我仍然需要[this](http://stackoverflow.com/questions/5469956/python-decorator-self-is-mixed-up)來確保自己是正確的。謝謝! – teeler

+0

@teeler哇,這個鏈接的第二個問題的答案是相當了不起的! – brandizzi

1

真誠的,我想你會用更好:

from functools import wraps 

def withbefore(fn): 
    def dec(bef): 
     fn._before_fn = bef 
     return bef 

    @wraps(fn) 
    def _wrapper(*args, **kwargs): 
     fn._before_fn(*args, **kwargs) 
     return fn(*args, **kwargs) 

    _wrapper.before = dec 
    return _wrapper 

這是更緊湊,更Pythonic,應該適用於所有情況。