看一段代码,下边这段代码用于将二叉搜索树转换为升序排列的双向链表:
""" # 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
关键字。