Search code examples
pythondecoratorpartial

Python decorators using functools.partial.. Where does "func" come from?


Given the following python code...

from functools import partial

def run_n( func = None, count = 1 ):
    print( "func", func )
    print( "count", count )
    if func is None:
        return partial( run_n, count = count )
    def wrapper():
        for _ in range( count ):
            func()
    return wrapper

@run_n( count = 2 )
def func():
    print( "Hello" )

func()

I get the following output..

func None
count 2
func <function func at 0x7f6b45572ca0>
count 2
Hello
Hello

My question is, give that the first invocation of the run_n decorator has func None, how does the second invocation acquire func as the right value. I'll add that this code, less my prints, was acquired from the public source code. It seems to me that I lack the understanding of how partial works but I still can't see how func suddenly acquires a value.


Solution

  • First, run_n gets called with func=None and n=2. Since func is None, it will take the first branch of the if statement. partial then creates, and returns, an anonymous function that behaves as if it was defined like this:

    def anonymous(*args, **kwargs):
        return run_n(*args, count=2, **kwargs)
    

    Then that function gets called with the function being defined (i.e, the function printing "hello") as its first argument. This immediately calls

    run_n(<func-printing-hello>,count=2)
    

    This time, run_n has been given a func argument, and returns the wrapper function that calls func-printing-hello n times. This wrapper, eventually, is what gets assigned to the global func name.