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.
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 helper
s, 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.