How do Python decorators take arguments, and why do they need three levels of nesting?
When the decorator itself needs a setting of its own, such as a repeat count, a prefix, or a timeout, the nesting gets one level deeper. The reason is simple: the setting has to be captured before the function is wrapped, so there is an extra step in the middle.
The three levels each have their own job. The outermost level is the factory, and it receives the settings, such as a count of three. It returns the decorator. The middle level is the decorator itself, and it receives the function that is being decorated, and it returns the wrapper. The innermost level is the wrapper, and it receives the arguments of the real call, passes them on to the original function, and returns the result.
Why do you need three levels rather than two? Because the wrapper has to remember the configuration, and that configuration arrives one step before the wrapper is created. The factory captures it and hands it down through a closure, so by the time the decorated function is called, the setting is already fixed and cannot change.
The @ syntax hides the middle step from you. Writing @repeat(3) above a function does exactly the same thing as writing repeat(3)(func), because the decorator factory is called with the settings first, and the result of that call is then applied to the function.
If the decorator needs no settings at all, then the factory level is unnecessary and the two-level form is enough. The simple rule to remember is that the number of levels equals the number of things the decorator is given before the function arrives. The earlier questions in this topic covered the two-level form, and this answer adds the extra level that configuration requires.