我正在尝试编写一个像@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}?
答案 0 :(得分:2)
实际上,它引发了一个TypeError。
无论如何,在使用函数运行装饰器时我也遇到了同样的错误。之所以发生这种情况,是因为当您使用@foo.before
修饰函数时,它将使用修饰函数作为参数调用self._before
函数。由于self._before
为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)
答案 1 :(得分: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,应该适用于所有情况。