Decorators With Parameters

Decorators with parameters?

The syntax for decorators with arguments is a bit different - the decorator with arguments should return a function that will take a function and return another function. So it should really return a normal decorator. A bit confusing, right? What I mean is:

def decorator_factory(argument):
def decorator(function):
def wrapper(*args, **kwargs):
funny_stuff()
something_with_argument(argument)
result = function(*args, **kwargs)
more_funny_stuff()
return result
return wrapper
return decorator

Here you can read more on the subject - it's also possible to implement this using callable objects and that is also explained there.

Python decorators with arguments

It is just another level of indirection, basically the code is equivalent to:

decorator = my_function("Name ")
decorated = decorator(text)
text = decorated

Without arguments, you already have the decorator, so

decorated = my_function(text)
text = decorated

Typing decorators that can be used with or without arguments

The issue comes from the first overload (I should have read the pyright message twice!):

@overload
def decorator(arg: F) -> F:
...

This overload accepts a keyword parameter named arg, while the implementation does not!

Of course this does not matter in the case of a decorator used with the @decorator notation, but could if it is called like so: fct2 = decorator(arg=fct).

Python >= 3.8

The best way to solve the issue would be to change the first overload so that arg is a positional-only parameter (so cannot be used as a keyword argument):

@overload
def decorator(arg: F, /) -> F:
...

With support for Python < 3.8

Since positional-only parameters come with Python 3.8, we cannot change the first overload as desired.

Instead, let's change the implementation to allow for a **kwargs parameter (an other possibility would be to add a keyword arg parameter). But now we need to handle it properly in the code implementation, for example:

def decorator(*args: Any, **kwargs: Any) -> Any:
if kwargs:
raise TypeError("Unexpected keyword argument")

# rest of the implementation here

Default arguments in a function when using decorators

In this case **kwargs is not about the function signature, it's about how you called it.
And your call

simple(10)

have only specified one positional argument.

Decorator (wrapper) knows nothing about the function and its default arguments.
It just passes the arguments it received further.

simple(*(10,), **{})

If you want to do some excercise and write decorator that is informed about the defaults of a function it is wrapping, I suggest to take a look at inspect.signature().

python decorator how to add parameters

You need a decorator factory, that is a function that returns the decorator:

from functools import wraps


def round_decimal(n):
def decorator(fn):
@wraps(fn)
def inner(*args, **kwargs):
return round(fn(*args, **kwargs), n)

return inner

return decorator


@round_decimal(3)
def func(a, b, c):
return a / b / c


print(func(1.33333, 3.44444, 4.555))

output:

0.085


Related Topics



Leave a reply



Submit