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