pythonrecursionpython-nonlocal

Does 'nonlocal' variable always inherit from the outer loop, even in recursive calls?


I have written some code and recently came across the need for 'nonlocal' when writing functions of functions (came across this when dealing with recursion).

For example:

def swapPairs(head): 
    head = None

    def helper(node): 
        nonlocal head 
        ...
        head= nex.next 
        return helper(node.next) 

My question is quite simple, as we call the recursion function helper(node.next) , and loop back up to nonlocal head - does head take the value of None (due to nonlocal head)? Or does it retain head = nex.next, which it was assigned to in the previous recursion call?

So I am trying to understand whether 'nonlocal head' would cause head to always take the value of whatever it was assigned to in the outer function, or is this not the case? And instead it is just a way to initialise head in the inner function so it only STARTS by taking the initial value as defined in the outer function.


Solution

  • The nonlocal and global declarations are for lexical scoping – loosely speaking, they only care about the source code layout.

    def swapPairs(head): 
        head = None         # `head` in lexically outer scope          <═╗
                            #                                            ║
        def helper(node):   #                                            ║
            nonlocal head   # nonlocal `head` for inner lexical scope  >═╣
            ...             #                                            ║
            head= nex.next  # nonlocal applies to every use            >═╝
            return helper(node.next)
    

    Notably, scoping is not at all concerned with the runtime nesting of the code; it does not care whether helper is called by swapPairs, a sequence of helpers, or some unrelated function: The name head inside helper is completely equivalent to the name head inside swapPairs.

    That means the head inside def helper will always refer to the head inside the swapPairs call that defined def helper. Once the first call to helper assigns head = nex.next that changes the head inside the swapPairs and subsequent calls to helper will see (and modify) this new value.