You can't pass it as its own name, but you can add it to the keywords.
def decorate(function):
def wrap_function(*args, **kwargs):
kwargs['str'] = 'Hello!'
return function(*args, **kwargs)
return wrap_function
@decorate
def print_message(*args, **kwargs):
print(kwargs['str'])
Alternatively you can name its own argument:
def decorate(function):
def wrap_function(*args, **kwargs):
str = 'Hello!'
return function(str, *args, **kwargs)
return wrap_function
@decorate
def print_message(str, *args, **kwargs):
print(str)
Class method:
def decorate(function):
def wrap_function(*args, **kwargs):
str = 'Hello!'
args.insert(1, str)
return function(*args, **kwargs)
return wrap_function
class Printer:
@decorate
def print_message(self, str, *args, **kwargs):
print(str)
Answer from Tor Valamo on Stack OverflowYou can't pass it as its own name, but you can add it to the keywords.
def decorate(function):
def wrap_function(*args, **kwargs):
kwargs['str'] = 'Hello!'
return function(*args, **kwargs)
return wrap_function
@decorate
def print_message(*args, **kwargs):
print(kwargs['str'])
Alternatively you can name its own argument:
def decorate(function):
def wrap_function(*args, **kwargs):
str = 'Hello!'
return function(str, *args, **kwargs)
return wrap_function
@decorate
def print_message(str, *args, **kwargs):
print(str)
Class method:
def decorate(function):
def wrap_function(*args, **kwargs):
str = 'Hello!'
args.insert(1, str)
return function(*args, **kwargs)
return wrap_function
class Printer:
@decorate
def print_message(self, str, *args, **kwargs):
print(str)
If you want the argument to be "optionally-injected", only in case the function actually takes it, use something like this:
import inspect
def decorate(func):
def wrap_and_call(*args, **kwargs):
if 'str' in inspect.getargspec(func).args:
kwargs['str'] = 'Hello!'
return func(*args, **kwargs)
return wrap_and_call
@decorate
def func1(str):
print "Works! - " + str
@decorate
def func2():
print "Should work, also."
python - Decorators with parameters? - Stack Overflow
How do I pass extra arguments to a Python decorator? - Stack Overflow
How does a decorator with arguments work behind the screen?
python - How can I pass arguments to decorator, process there, and forward to decorated function? - Stack Overflow
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.
Usage:
@decorator_factory("Some argument")
def function_to_be_decorated(args):
print(f"Do something with '{args}'.")
decorator_factory("Some argument") uses the given argument to create a standard, argumentless decorator. So the following block is functionally identical to the one above:
created_decorator = decorator_factory("Some argument")
@created_decorator
def function_to_be_decorated(args):
print(f"Do something with '{args}'.")
Edit : for an in-depth understanding of the mental model of decorators, take a look at this awesome Pycon Talk. well worth the 30 minutes.
One way of thinking about decorators with arguments is
@decorator
def foo(*args, **kwargs):
pass
translates to
foo = decorator(foo)
So if the decorator had arguments,
@decorator_with_args(arg)
def foo(*args, **kwargs):
pass
translates to
foo = decorator_with_args(arg)(foo)
decorator_with_args is a function which accepts a custom argument and which returns the actual decorator (that will be applied to the decorated function).
I use a simple trick with partials to make my decorators easy
from functools import partial
def _pseudo_decor(fun, argument):
def ret_fun(*args, **kwargs):
#do stuff here, for eg.
print ("decorator arg is %s" % str(argument))
return fun(*args, **kwargs)
return ret_fun
real_decorator = partial(_pseudo_decor, argument=arg)
@real_decorator
def foo(*args, **kwargs):
pass
Update:
Above, foo becomes real_decorator(foo)
One effect of decorating a function is that the name foo is overridden upon decorator declaration. foo is "overridden" by whatever is returned by real_decorator. In this case, a new function object.
All of foo's metadata is overridden, notably docstring and function name.
>>> print(foo)
<function _pseudo_decor.<locals>.ret_fun at 0x10666a2f0>
functools.wraps gives us a convenient method to "lift" the docstring and name to the returned function.
from functools import partial, wraps
def _pseudo_decor(fun, argument):
# magic sauce to lift the name and doc of the function
@wraps(fun)
def ret_fun(*args, **kwargs):
# pre function execution stuff here, for eg.
print("decorator argument is %s" % str(argument))
returned_value = fun(*args, **kwargs)
# post execution stuff here, for eg.
print("returned value is %s" % returned_value)
return returned_value
return ret_fun
real_decorator1 = partial(_pseudo_decor, argument="some_arg")
real_decorator2 = partial(_pseudo_decor, argument="some_other_arg")
@real_decorator1
def bar(*args, **kwargs):
pass
>>> print(bar)
<function __main__.bar(*args, **kwargs)>
>>> bar(1,2,3, k="v", x="z")
decorator argument is some_arg
returned value is None
Since you are calling the decorator like a function, it needs to return another function which is the actual decorator:
def my_decorator(param):
def actual_decorator(func):
print("Decorating function {}, with parameter {}".format(func.__name__, param))
return function_wrapper(func) # assume we defined a wrapper somewhere
return actual_decorator
The outer function will be given any arguments you pass explicitly, and should return the inner function. The inner function will be passed the function to decorate, and return the modified function.
Usually you want the decorator to change the function behavior by wrapping it in a wrapper function. Here's an example that optionally adds logging when the function is called:
def log_decorator(log_enabled):
def actual_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if log_enabled:
print("Calling Function: " + func.__name__)
return func(*args, **kwargs)
return wrapper
return actual_decorator
The functools.wraps call copies things like the name and docstring to the wrapper function, to make it more similar to the original function.
Example usage:
>>> @log_decorator(True)
... def f(x):
... return x+1
...
>>> f(4)
Calling Function: f
5
Just to provide a different viewpoint: the syntax
@expr
def func(...): #stuff
is equivalent to
def func(...): #stuff
func = expr(func)
In particular, expr can be anything you like, as long as it evaluates to a callable. In particular particular, expr can be a decorator factory: you give it some parameters and it gives you a decorator. So maybe a better way to understand your situation is as
dec = decorator_factory(*args)
@dec
def func(...):
which can then be shortened to
@decorator_factory(*args)
def func(...):
Of course, since it looks like decorator_factory is a decorator, people tend to name it to reflect that. Which can be confusing when you try to follow the levels of indirection.
I am learning python decorators and having some doubts with decorators with arguments. I will try to make my point clear (Or my doubts, better say) with examples:
If I execute the following piece of code:
def do_function_twice(func):
print("Inside decorator")
@functools.wraps(func)
def wrapper(*arg):
print("Inside wrapper")
func(*arg)
return func(*arg)
return wrapper
@do_function_twice
def print_msg(msg):
print(msg)
return f"What is up buddy"
I receive Inside decorator as I was expecting, because behind the scenes python did print_msg = do_function_twice(print_msg)
What if I try creating a decorator with an argument like the following?
def do_sth_if_true(bool_):
print("Inside do_sth_if_true")
def repeat_func(func):
print("Inside repeat_func")
func()
return func()
return repeat_func
@do_sth_if_true(True)
def yeah():
print("Yeah")If I execute the script I receive:
Inside do_sth_if_true Inside repeat_func Yeah Yeah
I can imagine it is doing yeah = do_sth_if_true(True)(yeah) behind the scenes (So it is calling the wrapping function, in this example called repeat_func. My question would be: How does python know which way to apply the decorator?
The reason is immediate after considering how the decorator transforms the function and that functions are objects themselves in Python.
Let's start from the latter.
Functions are objects:
This is immediate when we consider the meaning of two pairs of parenthesis after a function name. Consider this simple example (Python 3):
def func(x):
def func2(y):
return x + y + 1
return func2
result = func(5)(10)
print(result) # 15
Here "func" returns a function object "func2" and therefore you can use:
func(5)(10)
You can view this as calling first
func(5)
and applying "(10)" to the resulting object that is a function! So you have:
func2(10)
Now since both "x" and "y" are defined, "func2" can return the final value to "result".
Remember, this is all possible because functions are object themselves and because "func" returns a function object
func2
and not its result (it is not invoking the function on its own)
func2()
In short, that means that with wrapped functions the second set of arguments is for the inner function (if the wrapper returns the inner function object).
Decorators:
In your example, "main" calls "fun1" in the last line with
return fun1(decarg)
Due to the decorator
@dec(decarg)
In reality you can think of "fun1" as:
fun1 = dec(decarg)(fun1)
Therefore, the last line in "main" is equivalent to:
return dec(decarg)(fun1)(decarg)
With the previous explanation it should be trivial to find the problem!
dec(decarg)gets executed and returns a "_dec" function object; note that this "decarg" is the one passed in the first parenthesis and thus in the decorator._dec(fun1)gets executed and returns a "_fun" function object._fun(decarg)gets executed and invokes fun1(decargs) with the return statement and this will correctly translate in fun1(3) that is the result you get; note that this "decarg" is the one passed in the third parenthesis and thus when you invoke "fun1" in main.
You don't get 13 as a result because you don't invoke "fun1" with the result from
funarg = decarg + 7
as argument, but rather you invoke it with "decarg" that is passed to "_fun" as positional argument (funarg=decarg) from main.
Anyway, I have to thank you for this question, because I was looking for a neat way to pass an argument to a decorator only when invoking a function, and this works very nicely.
Here is another example that might help:
from functools import wraps
def add(addend):
def decorator(func):
@wraps(func)
def wrapper(p1, p2=101):
for v in func(p1, p2):
yield v + addend
return wrapper
return decorator
def mul(multiplier):
def decorator(func):
@wraps(func)
def wrapper(p1, p2=101):
for v in func(p1, p2):
yield v * multiplier
return wrapper
return decorator
def super_gen(p1, p2=101, a=0, m=1):
@add(a)
@mul(m)
def gen(p1, p2=101):
for x in range(p1, p2):
yield x
return gen(p1, p2)
Ok here is a simple explaination:
A simple decorator (no parameters) is a function that takes a function as argument and returns another function that will be called in place of the original one
A decorator that accepts a parameter is a function that takes that parameter and returns a simple decorator
Here you can build a simple decorator that adds 7 to the parameter of the decorated function:
def add7(func):
'''takes a func and returns a decorated func adding 7 to its parameter'''
def resul(x):
return func(x + 7)
return resul
You can use it that way:
def main(mainarg):
decarg = mainarg + 2
@add7
def fun1(funarg):
return funarg+3
return fun1(decarg)
main(1)
it returns as expected 13.
But you can easily build a parameterized decorator that will add an arbitrary value, but adding a level for processing the parameter:
def adder(incr):
'''Process the parameter (the increment) and returns a simple decorator'''
def innerdec(func):
'''Decorator that takes a function and returns a decorated one
that will add the passed increment to its parameter'''
def resul(val):
return func(val + incr)
return resul
return innerdec
You will then use it that way
def main(mainarg):
decarg = mainarg + 2
@adder(7)
def fun1(funarg):
return funarg + 3
return fun1(decarg)
main(1)
still returns 13
As others wrote here, a decorator is a syntactic sugar (i.e., makes programs easier to read, write, or understand) of a function that receives another function as a parameter and activates it from inside.
So, calling this “Add()” function with the decorator, like this:
@wrapper()
def Add(x: int, y: int):
return x + y
It is just like calling the “wrapper” function with the “Add” function as a variable. Like this:
wrapper(Add)(x,y) # pass x,y to wrapper that pass it to Add function.
|wrapper|
|----Add----|()
The best way (I think) to add parameters to a decorator, is by nesting it all under another function that holds a child decorator. For example:
@deco_maker(msg: str)
def Add(x: int, y: int):
return x + y
will be this:
deco_maker(msg)(Add)(x,y)
|--wrapper-|
|-wrapper_func-|
|---------Add-------|
Here is a simple wrapper decorator that log's function calls, without parameters, which can look like this:
def wrapper(func: Callable):
def wrapper_func(*args, **kwargs):
logging.DEBUG f"Function '{func.__name__}' called with args: {[str(arg) for arg in args]}."
value = func(*args, **kwargs)
return value
return wrapper_func
and here is the extended decorator with relevant logging parameters (log name and level for more flexibility):
def log_func_calls(logger_name: str, log_level: int):
def wrapper(func: Callable):
def wrapper_func(*args, **kwargs):
logger = logging.getLogger(logger_name)
logger.log(
level=log_level,
msg=f"Function '{func.__name__}' called with args: {[str(arg) for arg in args]}."
)
value = func(*args, **kwargs)
return value
return wrapper_func
return wrapper
Here is a full code example for a parameterized decorator for logging function calls, and the log file output print after it.
Example:
import logging
from typing import Callable
# define app logger with file and console handlers
def setup_logging():
logger = logging.getLogger('test_app')
logger.setLevel(logging.DEBUG)
# create file handler which logs even debug messages
fh = logging.FileHandler('test.log')
fh.setLevel(logging.DEBUG)
# create formatter and add it to the file handler
formatter = logging.Formatter('{asctime} | {name} | {levelname:^8s} | {message}', style='{')
fh.setFormatter(formatter)
# add the handler to the logger
logger.addHandler(fh)
return logger
# define a log decorator to trace function calls
def log_func_calls(logger_name: str, log_level: int):
def wrapper(func: Callable):
def wrapper_func(*args, **kwargs):
logger = logging.getLogger(logger_name)
logger.log(
level=log_level,
msg=f"Function '{func.__name__}' called with args: {[str(arg) for arg in args]}."
)
value = func(*args, **kwargs)
return value
return wrapper_func
return wrapper
# sample usage 1
@log_func_calls(logger_name='test_app', log_level=logging.DEBUG)
def Add(x: int, y: int):
return x + y
# sample usage 2
@log_func_calls(logger_name='test_app', log_level=logging.DEBUG)
def Sub(x: int, y: int):
return x - y
# a test run
def main():
logger = setup_logging()
logger.info("<<< App started ! >>>")
print(Add(50,7))
print(Sub(10,7))
print(Add(50,70))
logger.info("<<< App Ended ! >>>")
if __name__ == "__main__":
main()
And the log output:
...
2022-06-19 23:34:52,656 | test_app | DEBUG | Function 'Add' called with args: ['50', '7'].
2022-06-19 23:34:52,656 | test_app | DEBUG | Function 'Sub' called with args: ['10', '7'].
2022-06-19 23:34:52,657 | test_app | DEBUG | Function 'Add' called with args: ['50', '70'].
...
the short answer is no but you can modify the code of the decorator function; for example:
def decorator(func):
def inner(*args,**kwargs):
return func(*args,**kwargs)
return inner
this is a standard decorator function, let's change it a little.
you could do something like this:
def decorator(func,defaultdescription,defaultnum):
def inner(description=defaultdescription,num=defaultnum,*args,**kwargs):
print("function",func.__code__.co_name,"with description",description,"with number",num)
return func(*args,**kwargs)
return inner
so you can change the description of the function