递归生成器对于遍历非线性结构很有用。例如,让二叉树为 None 或值的元组,左树,右树。递归生成器是访问所有节点的最简单方法。示例:
tree = (0, (1, None, (2, (3, None, None), (4, (5, None, None), None))),
(6, None, (7, (8, (9, None, None), None), None)))
def visit(tree): #
if tree is not None:
try:
value, left, right = tree
except ValueError: # wrong number to unpack
print("Bad tree:", tree)
else: # The following is one of 3 possible orders.
yield from visit(left)
yield value # Put this first or last for different orders.
yield from visit(right)
print(list(visit(tree)))
# prints nodes in the correct order for 'yield value' in the middle.
# [1, 3, 2, 5, 4, 0, 6, 9, 8, 7]
编辑:将if tree 替换为if tree is not None 以捕获其他错误值作为错误。
编辑 2: 关于将递归调用放在 try: 子句中(@jpmc26 评论)。
对于坏节点,上面的代码只记录 ValueError 并继续。例如,如果(9,None,None) 被(9,None) 替换,则输出为
Bad tree: (9, None)
[1, 3, 2, 5, 4, 0, 6, 8, 7]
更典型的是在记录后重新加注,使输出成为
Bad tree: (9, None)
Traceback (most recent call last):
File "F:\Python\a\tem4.py", line 16, in <module>
print(list(visit(tree)))
File "F:\Python\a\tem4.py", line 14, in visit
yield from visit(right)
File "F:\Python\a\tem4.py", line 14, in visit
yield from visit(right)
File "F:\Python\a\tem4.py", line 12, in visit
yield from visit(left)
File "F:\Python\a\tem4.py", line 12, in visit
yield from visit(left)
File "F:\Python\a\tem4.py", line 7, in visit
value, left, right = tree
ValueError: not enough values to unpack (expected 3, got 2)
回溯给出了从根到坏节点的路径。可以包装原始的visit(tree) 调用以减少对路径的回溯:(根、右、右、左、左)。
如果递归调用包含在 try: 子句中,则会在树的每个级别重新捕获、重新记录和重新引发错误。
Bad tree: (9, None)
Bad tree: (8, (9, None), None)
Bad tree: (7, (8, (9, None), None), None)
Bad tree: (6, None, (7, (8, (9, None), None), None))
Bad tree: (0, (1, None, (2, (3, None, None), (4, (5, None, None), None))), (6, None, (7, (8, (9, None), None), None)))
Traceback (most recent call last):
... # same as before
多个日志记录报告可能更多的是噪音而不是帮助。如果想要到坏节点的路径,最简单的方法是将每个递归调用包装在它自己的 try: 子句中,并在每个级别引发一个新的 ValueError,到目前为止构建的路径。
结论:如果没有使用异常进行流控制(例如,可以使用 IndexError 进行),try: 语句的存在和位置取决于人们想要的错误报告。