Reputation: 13
I'm trying to write a decorator that works like @property, but running into some problems.
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)
Its a simple chaining decorator. The @property/@.setter syntax is exactly what I'm trying to clone.
This works:
@withbefore
def foo():
...
@foo.before
def beforefoo():
...
But on a class it doesn't:
class Weee(object):
@withbefore
def do_stuff(self):
pass
@do_stuff.before
def before_do_stuff(self):
pass
It raises an import error.
TypeError: 'NoneType' object is not callable
How can i correctly emulate @property/.{setter,getter,deleter} ?
Upvotes: 1
Views: 1613
Reputation: 4560
Sincerely, I think you'd be better off with:
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
It is more compact, more Pythonic and should work OK for all cases.
Upvotes: 1
Reputation: 27050
Actually, it raises a TypeError.
Anyway, I got the same error when running your decorator with functions, too. It happens because, when you decorate a function with @foo.before
, it will call the self._before
function with the decorated function as parameter. Since self._before
is None
, it will raise the error.
There are various solutions for it. My favorite is to set a different default value to self._before
- a function which will set the self._before
value:
class Dec(object):
def __init__(self, fn):
self._fn = fn
def setbefore(b):
self._before = b
self._before = self.default_before = setbefore
Of course, this function should not be called when the Dec
object is called so we change the __call__
method:
def __call__(self, *args, **kwargs):
if self._before != self.default_before:
self._before(*args, **kwargs)
self._fn(*args, **kwargs)
Upvotes: 2