4.3. Decorator Types

By type:

  • Function decorators

  • Class decorators

By decorated object:

  • Decorating function

  • Decorating class

  • Decorating method

By wrapper type:

  • Wrapper function

  • Wrapper class

  • Wrapper method

By number of arguments:

  • Without arguments

  • With arguments

4.3.1. Decorator Types

  • Function decorators

  • Class decorators

  • In this example:

    • obj is a decorated object

    • doesn't matter, whether is a function, class or method

>>> def mydecorator(obj):
...     ...
>>> class MyDecorator:
...     def __init__(self, obj):
...         ...

4.3.2. Wrapper Type

  • Wrapper function

  • Wrapper class

  • Wrapper method

  • In this example:

    • obj is a decorated object

    • doesn't matter, whether is a function, class or method

  • If obj and Wrapper are classes, Wrapper can inherit from obj (to extend it)

>>> def mydecorator(obj):
...     def wrapper(*args, **kwargs):
...         ...
...     return wrapper
>>> def mydecorator(obj):
...     class Wrapper:
...         def __init__(self, *args, **kwargs):
...             ...
...     return Wrapper
>>> class MyDecorator:
...     def __init__(self, obj):
...         ...
...
...     def __call__(self, *args, **kwargs):
...         ...

4.3.3. Decorated Object

  • Decorating function (by convention func or fn)

  • Decorating class (by convention cls)

  • Decorating method (by convention mth, meth or method)

>>> def mydecorator(func):
...     ...
>>> def mydecorator(cls):
...     ...
>>> def mydecorator(mth):
...     ...
>>> class MyDecorator:
...     def __init__(self, func):
...         ...
>>> class MyDecorator:
...     def __init__(self, cls):
...         ...
>>> class MyDecorator:
...     def __init__(self, mth):
...         ...

4.3.4. Usage

>>> @mydecorator
... def myfunction(*args, **kwargs):
...     ...
>>> class MyClass:
...     @mydecorator
...     def mymethod(self, *args, **kwargs):
...         ...
>>> @mydecorator
... class MyClass:
...     ...
>>> @MyDecorator
... def myfunction(*args, **kwargs):
...     ...
>>> class MyClass:
...     @MyDecorator
...     def mymethod(self, *args, **kwargs):
...         ...
>>> @MyDecorator
... class MyClass:
...     ...

4.3.5. Arguments

  • Without arguments

  • With arguments

>>> @mydecorator
... def myfunction(*args, **kwargs):
...     ...
>>> @MyDecorator
... def myfunction(*args, **kwargs):
...     ...
>>> @mydecorator('arg1', 'arg2')  
... def myfunction(*args, **kwargs):
...     ...
>>> @MyClass('arg1', 'arg2')  
... def myfunction(*args, **kwargs):
...     ...

4.3.6. Assignments

Code 4.44. Solution
"""
* Assignment: Decorator Syntax About
* Complexity: easy
* Lines of code: 5 lines
* Time: 5 min

English:
    1. Create decorator `mydecorator`
    2. Decorator should have `wrapper` with `*args` and `**kwargs` parameters
    3. Wrapper should call original function with it's original parameters,
       and return its value
    4. Decorator should return `wrapper` function
    5. Run doctests - all must succeed

Polish:
    1. Stwórz dekorator `mydecorator`
    2. Dekorator powinien mieć `wrapper` z parametrami `*args` i `**kwargs`
    3. Wrapper powinien wywoływać oryginalną funkcję z jej oryginalnymi
       parametrami i zwracać jej wartość
    4. Decorator powinien zwracać funckję `wrapper`
    5. Uruchom doctesty - wszystkie muszą się powieść

Tests:
    >>> import sys; sys.tracebacklimit = 0
    >>> from inspect import isfunction

    >>> assert isfunction(mydecorator), \
    'Create mydecorator() function'

    >>> assert isfunction(mydecorator(lambda: ...)), \
    'mydecorator() should take function as an argument'

    >>> @mydecorator
    ... def echo(text):
    ...     return text

    >>> echo('hello')
    'hello'
"""

# type: Callable[[Callable], Callable]
def mydecorator():
    pass

Code 4.45. Solution
"""
* Assignment: Decorator Syntax Disable
* Complexity: easy
* Lines of code: 1 lines
* Time: 5 min

English:
    1. Modify decorator `disable`
    2. Decorator raises `PermissionError` and does not execute function
    3. Run doctests - all must succeed

Polish:
    1. Zmodyfikuj dekorator `disable`
    2. Dekorator podnosi `PermissionError` i nie wywołuje funkcji
    3. Uruchom doctesty - wszystkie muszą się powieść

Tests:
    >>> import sys; sys.tracebacklimit = 0
    >>> from inspect import isfunction

    >>> assert isfunction(disable), \
    'Create disable() function'

    >>> assert isfunction(disable(lambda: ...)), \
    'disable() should take function as an argument'

    >>> @disable
    ... def echo(text):
    ...     print(text)

    >>> echo('hello')
    Traceback (most recent call last):
    PermissionError: Function is disabled
"""


# type: Callable[[Callable], Callable]
def disable(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)

    return wrapper