2012-05-28 36 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,应该适用于所有情况。