分解您通常理解的函数的一种好方法是使用 python 调试器。这里添加了评论:
-> def flatten(nested):
(Pdb) l
1 -> def flatten(nested):
2 try:
3 for sublist in nested:
4 for element in flatten(sublist):
5 yield element
6 except TypeError:
7 yield nested
8
9 import pdb; pdb.set_trace()
10 list(flatten([[1,2],3]))
11
(Pdb) a
nested = [[1, 2], 3]
上面,我们刚刚输入了函数,参数是[[1, 2], 3]
. 让我们使用 pdb 的step函数单步执行该函数,进入我们应该遇到的任何递归调用:
(Pdb) s
> /Users/michael/foo.py(2)flatten()
-> try:
(Pdb) s
> /Users/michael/foo.py(3)flatten()
-> for sublist in nested:
(Pdb) s
> /Users/michael/foo.py(4)flatten()
-> for element in flatten(sublist):
(Pdb) s
--Call--
> /Users/michael/foo.py(1)flatten()
-> def flatten(nested):
(Pdb) a
nested = [1, 2]
我们已经进入了 的一个内部框架flatten
,参数在哪里[1, 2]
。
(Pdb) s
> /Users/michael/foo.py(2)flatten()
-> try:
(Pdb) s
> /Users/michael/foo.py(3)flatten()
-> for sublist in nested:
(Pdb) s
> /Users/michael/foo.py(4)flatten()
-> for element in flatten(sublist):
(Pdb) s
--Call--
> /Users/michael/foo.py(1)flatten()
-> def flatten(nested):
(Pdb) a
nested = 1
两帧后,参数1
不再是可迭代的。这应该很有趣……</p>
(Pdb) s
> /Users/michael/foo.py(2)flatten()
-> try:
(Pdb) s
> /Users/michael/foo.py(3)flatten()
-> for sublist in nested:
(Pdb) s
TypeError: "'int' object is not iterable"
> /Users/michael/foo.py(3)flatten()
-> for sublist in nested:
(Pdb) s
> /Users/michael/foo.py(6)flatten()
-> except TypeError:
(Pdb) s
> /Users/michael/foo.py(7)flatten()
-> yield nested
(Pdb) s
--Return--
> /Users/michael/foo.py(7)flatten()->1
-> yield nested
好的,所以由于except TypeError
,我们只是产生了论点本身。上一帧!
(Pdb) s
> /Users/michael/foo.py(5)flatten()
-> yield element
(Pdb) l
1 def flatten(nested):
2 try:
3 for sublist in nested:
4 for element in flatten(sublist):
5 -> yield element
6 except TypeError:
7 yield nested
8
9 import pdb; pdb.set_trace()
10 list(flatten([[1,2],3]))
11
yield element
当然会产生1
,所以一旦我们的最低帧命中 a TypeError
,结果就会一直传播到堆栈的最外层帧flatten
,这会在移动到外部可迭代的其他部分之前将其产生给外部世界。