Декоратор, конечно, должен получить в качестве параметра экземпляр, к которому он привязан - и код будет намного чище, если у самой декорированной функции будет один параметр, который будет передан как экземпляр, к которому она привязана тоже: эквивалент self
, если он был определен как метод. Python не вставит его автоматически, но он может называться self
, поэтому он легко читается;
class Transition:
...
f = Transition
def bind(instance):
def decorator(func):
def wrapper (*args, **kwargs):
return func(instance, *args, **kwargs)
setattr(instance, func.__name__, wrapper)
return wrapper
return decorator
@bind(f)
def condition(self, ...):
...
Если вам нужен более плоский декоратор, вы можете использовать functools.partial
- и тогда я также использую functools.wraps
, так как хорошие декораторы должны использовать его в любом случае:
import functools
...
def bind(func, instance=None):
if instance is None:
return functools.partial(bind, instance=func)
@functools.wraps(func)
def wrapper(*args, **kw):
return func(instance, *args, **kw)
setattr(instance, func.__name__, wrapper)
return wrapper
Это работает, потому что в Python, когда функция приписывается непосредственно к экземпляру, она ведет себя точно так же, как любой обычный атрибут: Python будет извлекать функцию, а затем вызывать ее без изменений - не изменяя ее на метод и не вставляя self
аргумент). Мы делаем это, удерживая экземпляр как нелокальную переменную внутри кода декоратора.