Lets say decorator
has this definition:
def decorator(func: Callable[[], Any]) -> Callable[[], Any]:
def wrapper():
...
func()
...
return wrapper
When doing @decorator
with, say a function like:
@decorator
def thing():
pass
This pretty much gets converted to
def thing():
pass
thing = decorator(thing)
When calling decorator
, this will define a function that wraps the function we just passed (the decorated one), and then it immediately returns that function definition that was defined. Now thing
gets redefined to a completely different function:
def thing():
...
func() # original thing()
...
Now a bit of a more complex decorator:
def decorator2(name: str):
def outer_wrapper(func: Callable[[str], Any]) -> Callable[[], None]:
def wrapper() -> None:
func(name)
return wrapper
return outer_wrapper
Which may be used like this:
@decorator2("juan")
def thing(name: str):
print(f"name: {name}")
This is what actually happens when doing this:
def thing(name: str):
print(f"name: {name}")
thing = decorator2("juan")(thing)
First, the decorator gets called with the parameter that was passed into it (name). On this call, decorator2
returns the outer_wrapper
function. Which wraps another function (that's it entire purpose).
As seen in the definition, outer_wrapper
takes a function as a parameter, which is the function that is being decorated.
# > decorator2(name)
# > outer_wrapper(func)
thing = decorator2("juan")(thing)
At this point, thing
becomes:
def thing():
...
func("juan") # original thing(name)
...
Important to note that calling thing
now will always return None
, because the wrapper
decorator doesn't return the result of func()
.