Python Decorators 102

Now that you know your way around decorators, you're starting to have more complex use cases and you want to figure out how to pass a parameter to your decorator.

Using our @metadata example from Python Decorators 101, we add some extra nesting like this:

import typing
from functools import wraps


def metadata(custom_parameter: str):
    def decorate(func: typing.Callable) -> typing.Callable:
        @wraps(wrapped=func)
        def wrapper(*args, **kwargs):
            metadata_ = {
                "function_name": func.__name__,
                "docstring": func.__doc__,
                "custom_parameter": custom_parameter
            }
            print(metadata_)
            return func(*args, **kwargs)
        return wrapper
    return decorate
decorator.py

Let's decorate a simple function with this new version of @metadata:

import typing
from functools import wraps, reduce
from operator import add


def metadata(...
    ...
    ...
    ...
    return decorate
    
    
@metadata(custom_parameter="Lovely jobly")
def add_all(*integers) -> int:
    """ I am a docstring """
    return reduce(add, integers)
decorator.py

As usual, let's go into the REPL and press play on that thing:

# python -i decorator.py 
>>> add_all(1,2,3)
{'function_name': 'add_all', 'docstring': ' I am a doctring ', 'custom_parameter': 'Lovely jobly'}
6
>>>

See? Your metadata include the decorator parameter value and the result of adding 1 + 2 + 3. It's like magic.

それは 魔法の


Oh, and did you know you can decorate a class too?

@metadata(custom_parameter="I am custom")
class DecorateMe:
    """ I am the class """
    def print_one(self) -> str:
        print("one")

Let's go into the REPL and...

# python -i decorator.py 
>>> decorate_me = DecorateMe()
{'function_name': 'DecorateMe', 'docstring': ' I am the class ', 'custom_parameter': 'I am custom'}

You now can do something when the class is instantiated. There are also other ways to do this with inheritance etc but that's another story for another day.

別の日