Python 的非本地关键字 - 这是好习惯吗?
Python's nonlocal keyword - is this good practice?
考虑一个简单的情况,例如在 BST 中找到第 k 个最小的元素。
在我下面的解决方案中:
class Solution:
def kthSmallest(self, root: TreeNode, k: int) -> int:
i = 0
ans = -1
def traverse_inorder(root):
nonlocal i
nonlocal ans
if not root:
return
traverse_inorder(root.left)
i += 1
if i == k:
ans = root.val
return
traverse_inorder(root.right)
traverse_inorder(root)
return ans
我对 i
和 ans
使用 nonlocal
是好的做法吗?我这样做是为了跟踪在到达最左边的节点(最小值)后我遍历了多少元素。
另一种解决方案是将 i
和 ans
作为 class:
的成员变量
class Solution:
def kthSmallest(self, root: TreeNode, k: int) -> int:
self.i = 0
self.ans = -1
def traverse_inorder(root):
# etc etc
两种方法是否等价?一种做法是否比另一种更好,为什么?
我认为,在您描述的情况下,nonlocal
您描述的方法客观地从显示的两个中更有意义。您希望在函数外部有一个计数器,并希望在找到结果时跟踪结果,而不管您在递归中的什么位置。
nonlocal
将该信息完全封装在专用于外部函数的特定 运行 的名称空间中。这里真的没有缺点。
使用实例属性可以使该信息对使用该实例的任何人可用。这在概念上是不必要的,稍微慢一点,而且不是线程安全的。虽然线程安全在 Python 中通常不是一个问题,但它确实会使您的代码变得不那么健壮。在那个和封装之间,我肯定会远离这种方法。
我在这里建议第三种可能性:使用 return 值。在那种情况下,您真的不需要任何外部名称空间。我不一定推荐使用 nonlocal
,但值得一提。这是一个示例实现,如您所见,它比您的解决方案冗长得多:
class Solution:
def kthSmallest(self, root: TreeNode, k: int) -> int:
def traverse_inorder(root, target):
if not root:
return 0, None
left_count, item = traverse_inorder(root.left, target)
if left_count == target - 1:
return left_count + 1, root.val
elif left_count < target:
right_count, item = traverse_inorder(root.right, target - left_count - 1)
return left_count + right_count + 1, item
else: # left_count == target
return left_count, item
count, ans = traverse_inorder(root, k)
if count < k:
raise ValueError('Insufficient elements')
return ans
有很多方法可以用 return 值来做到这一点。在这里,我计算每个子树中的元素数量,直到达到目标值。非 none 项目仅在找到确切数量的元素后才被 return 编辑。
考虑一个简单的情况,例如在 BST 中找到第 k 个最小的元素。
在我下面的解决方案中:
class Solution:
def kthSmallest(self, root: TreeNode, k: int) -> int:
i = 0
ans = -1
def traverse_inorder(root):
nonlocal i
nonlocal ans
if not root:
return
traverse_inorder(root.left)
i += 1
if i == k:
ans = root.val
return
traverse_inorder(root.right)
traverse_inorder(root)
return ans
我对 i
和 ans
使用 nonlocal
是好的做法吗?我这样做是为了跟踪在到达最左边的节点(最小值)后我遍历了多少元素。
另一种解决方案是将 i
和 ans
作为 class:
class Solution:
def kthSmallest(self, root: TreeNode, k: int) -> int:
self.i = 0
self.ans = -1
def traverse_inorder(root):
# etc etc
两种方法是否等价?一种做法是否比另一种更好,为什么?
我认为,在您描述的情况下,nonlocal
您描述的方法客观地从显示的两个中更有意义。您希望在函数外部有一个计数器,并希望在找到结果时跟踪结果,而不管您在递归中的什么位置。
nonlocal
将该信息完全封装在专用于外部函数的特定 运行 的名称空间中。这里真的没有缺点。
使用实例属性可以使该信息对使用该实例的任何人可用。这在概念上是不必要的,稍微慢一点,而且不是线程安全的。虽然线程安全在 Python 中通常不是一个问题,但它确实会使您的代码变得不那么健壮。在那个和封装之间,我肯定会远离这种方法。
我在这里建议第三种可能性:使用 return 值。在那种情况下,您真的不需要任何外部名称空间。我不一定推荐使用 nonlocal
,但值得一提。这是一个示例实现,如您所见,它比您的解决方案冗长得多:
class Solution:
def kthSmallest(self, root: TreeNode, k: int) -> int:
def traverse_inorder(root, target):
if not root:
return 0, None
left_count, item = traverse_inorder(root.left, target)
if left_count == target - 1:
return left_count + 1, root.val
elif left_count < target:
right_count, item = traverse_inorder(root.right, target - left_count - 1)
return left_count + right_count + 1, item
else: # left_count == target
return left_count, item
count, ans = traverse_inorder(root, k)
if count < k:
raise ValueError('Insufficient elements')
return ans
有很多方法可以用 return 值来做到这一点。在这里,我计算每个子树中的元素数量,直到达到目标值。非 none 项目仅在找到确切数量的元素后才被 return 编辑。