1

我正在尝试编写一个像@property 一样工作的装饰器,但遇到了一些问题。

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

它会引发导入错误。

TypeError: 'NoneType' object is not callable

我怎样才能正确模拟 @property/.{setter,getter,deleter} ?

4

2 回答 2

2

实际上,它引发了 TypeError。

无论如何,在使用函数运行装饰器时,我也遇到了同样的错误。发生这种情况是因为,当您使用 装饰函数时@foo.before,它将self._before以装饰函数作为参数调用该函数。由于self._beforeis None,它将引发错误。

有各种解决方案。我最喜欢的是设置一个不同的默认值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)
于 2012-05-28T17:00:46.973 回答
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,并且在所有情况下都可以正常工作。

于 2012-05-28T17:06:48.270 回答