Decorators
Decorators can be applied to classes or functions/methods. A decorator is a callable, so a function or a class
implementing __call__
. Decorator can accept parameters, when a decorator factory returns a specific decorator.
The decorator syntax of Python is syntactic sugar for a function call.
See also decorators offered by pyTooling.
Hint
The predefined wraps()
decorator should be used when creating wrapping or replacing decorators, so
the name and doc-string of the callable is preserved and decorators can be chained.
Function-based without Parameter |
Function-based with Parameter(s) |
Class-based with Parameter(s) |
---|---|---|
from functools import wraps
F = TypeVar("F", Callable)
def decorator(func: F) -> F:
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
#
|
from functools import wraps
F = TypeVar("F", Callable)
def decorator_factory(param: int) -> Callable:
def specific_decorator(func: F) -> F:
@wraps(func)
def wrapper(*args, **kwargs):
kwargs["param"] = param
return func(*args, **kwargs)
return wrapper
return specific_Decorator
#
|
from functools import wraps
F = TypeVar("F", Callable)
class decoratorclass:
_param: int
def __init__(self, param: int) -> None:
self._param = param
def __call__(self, func: F) -> F:
@wraps(func)
def wrapper(*args, **kwargs):
kwargs["param"] = self._param
return func(*args, **kwargs)
return wrapper
|
@decorator
def foo(param: int) -> bool:
pass
|
@decorator_factory(10)
def foo(param: int) -> bool:
pass
|
@decoratorclass(10)
def foo(param: int) -> bool:
pass
|
def foo(param: int) -> bool:
pass
foo = decorator(foo)
|
def foo(param: int) -> bool:
pass
foo = decorator(10)(foo)
|
def foo(param: int) -> bool:
pass
foo = decoratorclass(10)(foo)
|
Usecase
Modifying Decorator
A modifying decorator returns the original, but modified language item. Existing fields might be modified or new fields might be added to the language item. It supports classes, functions and methods.
F = TypeVar("F", Callable)
def decorator(func: F) -> F:
func.__field__ = ...
return func
@decorator
def function(param: int) -> bool:
pass
class C:
@decorator
def method(self, param: int) -> bool:
pass
See also
The predefined wraps()
decorator is a modifying decorator because it copies the __name__
and
__doc__
fields from the original callable to the decorated callable.
Replacing Decorator
A replacing decorator replaces the original language item by a new language item. The new item might have a similar or completely different behavior as the original item. It supports classes, functions and methods.
F = TypeVar("F", Callable)
def decorator(func: F) -> F:
def replacement(*args, **kwargs):
pass
return replacement
@decorator
def function(param: int) -> bool:
pass
class C:
@decorator
def method(self, param: int) -> bool:
pass
See also
The predefined property()
decorator is a replacing decorator because it replaces the method with a descriptor
implementing getter for a read-only property. It’s a special cases, because it’s also a wrapping decorator as the
behavior of the original method is the behavior of the getter.
Wrapping Decorator
Todo
TUTORIAL::Wrapping decorator
F = TypeVar("F", Callable)
def decorator(func: F) -> F:
def wrapper(*args, **kwargs):
# ...
return func(*args, **kwargs)
return replacement
@decorator
def function(param: int) -> bool:
pass
class C:
@decorator
def method(self, param: int) -> bool:
pass
Without Parameters
Function-based without Parameters
Todo
TUTORIAL::Function-based without parameters - write a tutorial
F = TypeVar("F", Callable)
def decorator(func: F) -> F:
def wrapper(*args, **kwargs):
# ...
return func(*args, **kwargs)
return replacement
With Parameters
Function-based with Parameters
Todo
TUTORIAL::Function-based with parameters - write a tutorial
F = TypeVar("F", Callable)
def decorator_factory(param: int) -> Callable:
def decorator(func: F) -> F:
def wrapper(*args, **kwargs):
# ...
return func(*args, **kwargs)
return replacement
return decorator
Class-based with Parameters
A decorator accepting parameters can also be implemented with a class providing __call__
, so it’s a callable.
Todo
TUTORIAL::Class-based - write a tutorial
from functools import wraps
F = TypeVar("F", Callable)
class decoratorclass:
_param: int
def __init__(self, param: int) -> None:
self._param = param
def __call__(self, func: F) -> F:
@wraps(func)
def wrapper(*args, **kwargs):
kwargs["param"] = self._param
return func(*args, **kwargs)
return wrapper