Does anyone know if it's possible to pass arguments to a decorator while calling a function in Python ?
Until now, I only saw this kind of thing on a function definition :
@decorator("This is a decorator", 66)
def func(a: int, b: int) -> None:
pass
But I was wondering if it was possible to do it while calling a function.
something like this :
func(a, b)(66) # 66 is passed by argument to @decorator
For those who are wondering why I want to do this, it's because I'm proceeding someone else's work who used decorators a lot in his code. But until now he only had to pass arguments at function definition because the function was only called once. The decorators are used to print information logs about the context the function is used but in my case, since the function can be called at different places, the context can be different in function of where the function is called.
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'].
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With