看一段代码,下边这段代码用于将二叉搜索树转换为升序排列的双向链表:
"""
# Definition for a Node.
class Node:
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
"""
class Solution:
def treeToDoublyList(self, root: 'Node') -> 'Node':
if not root:
return root
# 递归函数,用于中序遍历和连接节点
def recursion(root):
nonlocal prev, head
if root:
# 递归遍历左子树
recursion(root.left)
# 将当前节点与前一个节点连接起来
if prev:
prev.right = root
root.left = prev
else:
# 当前节点是最左的节点,将其赋值给head
head = root
prev = root
# 递归遍历右子树
recursion(root.right)
prev = None # 前一个节点
head = None # 头节点
recursion(root)
# 将头节点和尾节点连接起来,形成循环双向链表
head.left = prev
prev.right = head
return head
去掉nonlocal,会报错:
UnboundLocalError: cannot access local variable 'prev' where it is not associated with a value
解释:
nonlocal
是Python中的关键字,用于声明一个嵌套函数中的变量是来自于其外部函数的局部作用域,而不是来自于全局作用域。
在这段代码中,prev
和head
是在recursion
函数之外定义的变量。然而,我们需要在recursion
函数中对它们进行修改。为了在recursion
函数中能够访问并修改这些变量,我们使用nonlocal
关键字来声明它们。
这样,在recursion
函数中对prev
和head
的修改将会影响到它们在treeToDoublyList
方法中的值。这使得我们能够在recursion
函数中正确地处理节点的连接和循环链表的生成。
再看下边一段二叉树中序遍历的代码:
class Solution:
def inorderSearch(self, root: 'Node') -> 'Node':
if not root:
return None
res = []
def inorder(node):
if node:
inorder(node.left)
res.append(node.val)
inorder(node.right)
inorder(root)
return res
res同样是在外层函数中定义然后在内层函数中使用,为什么这里没有加上nonlocal也没有报错?
在这段代码中,
res
是一个列表,它是在外层函数treeToDoublyList
中定义的。在 Python 中,列表是可变对象,可以在函数内部通过引用进行修改。
在inorder
函数中,虽然res
是在外层函数中定义的,但是我们只是对其进行修改,没有重新赋值。因此,Python 会默认将res
视为外层函数中定义的变量,而不是新的局部变量。
与nonlocal
不同,nonlocal
用于改变外层函数的局部变量,而不是修改可变对象的值。因此,在使用nonlocal
时,我们需要明确告诉 Python 我们要修改的是外层函数的局部变量。而对于可变对象,Python 默认会将其视为外层函数的局部变量,因此不需要使用nonlocal
。
总结起来,列表等可变对象可以在函数内部进行修改,而不需要使用nonlocal
;而对于需要修改外层函数的局部变量时,我们需要使用nonlocal
关键字。