So I have this code:
A decorator which adds an all_input attribute to the wrapped function.
This attribute collects any input passed to the function.
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
wrapper.all_input = 
The nice thing of Python is that it has a set of rules to trate its objects, and very little exceptions to these rules.
In this case, the function that is decorated is just a normal Python object. One that happens to be also callable.
What happens in the line
wrapper.all_input =  inside collect_input is that it sets an attribute on the object - at that point named
wrapper - but which is the object that will be returned and take the place of the
foo function in the global scope. That is the how decorators work.
So lets got trough it step by step to make it as clear as possible:
When running the code above, it defines the
collect_input function - which is designed to be used as a decorator.
Then it defines
foo function, but before it is added to the global scope, it is passed into the
collect_input function. That is what the "@" syntax does. Prior to its existence, the way to decorate functions as to first define a function, and then, replace it for the decorator's return value with a normal assignment. So, the code above is the same as:
def foo(...): ...
foo = collect_input(foo)
Inside "collect_input", the original
foo func will be called inside the new
wrapper function. This
wrapper function: a new (function) object created each time the decorator
collect_input is called is the object that will take the place of the outermost
foo definition. You can see that inside the code for
wrapper there is extra code to do exactly what the
collect_input is meant to: annotate the input parameters in a list, attached to itself and then resume the call to the original function -
foo in this case.
wrapper object that is returned by
collect_input takes the place of
foo, but has the
all_inputs list attached to it inside the decorator call. So it can be accessed in the global scope as an attribute of the
foo object - regardless of where it was defined. Note that you can't, outside the function, use the names
wrapper, as expected.