Python Decorators 102

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.

別の日