Decorators are great until you start to try to explain to somebody what is actually happening.
"So, a decorator is any function that takes a function and returns a function (in a sane world, one that is at least loosely based on the function it takes.)"
"But if you want to modify the actual parameter values passed to the function you are decorating, you will have to make a wrapper that can make those modifications."
"So then it's a function that takes a function and returns a wrapper around that function (or one based on it, you can still modify the decorated and wrapped function.)"
"And if you want your decorator to take arguments. Well, a decorator only takes a function as an argument. So you really need a function factory that takes arguments and returns a decorator."
"So basically a decorator is a function factory that returns a function that takes a function and returns a wrapper containing that function. Easy."
“Oh, but wait, if you want to modify the result of the original function you actually need your wrapper to take the arguments, pass them to the function while executing the function, capture the results, modify them, and finally return them.”
“You know what, Decorators are magic. Copy and paste this template and change the parts you need to change based on the comments.”
import functools
# This is the function you will use with @function
def decorator_factory(*args, **kwargs):
# Out here are the arguments passed to the decorator
# Remove this print, use these to change the behavior of the
# decorator. You can replace *args/**kwargs with specifics if you like.
print(f"The decorator got args: {args}")
print(f"The decorator got kwargs: {kwargs}")
pass_this_along = kwargs["pass_this_along"]
def decorator(func): # You probably don't need to change this line
def wrapper(*args, **kwargs):
# Example of modifying function kwargs with
kwargs["keyword_only"] = pass_this_along
# These are the args/kwargs passed to the function
print(f"The wrapper saw the function args: {args}")
print(f"The wrapper saw the function kwardargs: {kwargs}")
# Call the actual function with the now modified args
result = func(*args, **kwargs)
print(f"The wrapper saw the function result: {result}")
# Modify the results if you like
return result.upper()
# Since the wrapper will obscure the signature
# Use functools.wraps to expose the inner signature
return functools.wraps(func)(wrapper)
return decorator
@decorator_factory("arg1", thing="stuff", pass_this_along="keyword only value")
def thing(func_arg1, *, keyword_only="keyword only default."):
print(f"The function got arg: {func_arg1}")
print(f"The function got the keywork only argument: {keyword_only}")
return "lower case function result"
result = thing("more stuff!")
print(f"The actual return value ended up as: {result}")
Comments
Post a Comment