为何在LeetCode题中需用nonlocal避免UnboundLocalError?
先给你捋清楚Python和JavaScript在闭包变量作用域上的核心差异,就能明白为啥必须加nonlocal了:
1. Python的变量赋值规则
Python里有个关键规则:如果函数内部对某个变量执行了赋值操作(比如res += node.val本质是res = res + node.val),Python会默认把这个变量当成当前函数的局部变量,哪怕外层作用域已经定义了同名变量。
回到你的代码:如果去掉nonlocal res,dfs函数里的res += node.val会触发Python的局部变量判定——它会认为res是dfs自己的局部变量,但你在赋值之前根本没在dfs里定义过res,自然就会抛出UnboundLocalError。
2. JavaScript的闭包逻辑
JS的规则不一样:内层函数可以直接访问外层函数的变量,而且只要你没有在内部用var/let/const重新声明同名变量,修改外层变量的操作(比如res += ...)就会直接作用于外层的那个变量,不会触发局部变量的重新声明。这就是你在JS里不用额外关键字就能实现闭包修改的原因。
3. 再看你的代码里nonlocal的作用
nonlocal res这句话就是明确告诉Python:「这个res不是我dfs函数的局部变量,你去上一层的作用域(也就是rangeSumBST函数的作用域)找它」。这样Python就不会把res当成dfs的局部变量,修改操作就能正确作用于外层定义的res了。
替代方案(不用nonlocal的写法)
如果你不想用nonlocal,也可以用可变类型(比如列表)来存储结果,因为修改可变类型的元素不需要声明变量作用域:
class Solution: def rangeSumBST(self, root: Optional[TreeNode], low: int, high: int) -> int: res = [0] # 用列表存,因为列表是引用类型 def dfs(node): if not node: return if low <= node.val <= high: res[0] += node.val dfs(node.left) dfs(node.right) dfs(root) return res[0]
不过这种写法可读性不如用nonlocal直接明了,还是推荐用nonlocal的写法。
内容的提问来源于stack exchange,提问作者Alex Zelinsky

