为了理解发生了什么,它有助于打印生成器的输出,以获得几个顺序增加的值n:
>>> for i in range(5):
print(list(gen(i)))
[()]
[(1,)]
[(1, 1), (2,)]
[(1, 1, 1), (1, 2), (3,)]
[(1, 1, 1, 1), (1, 1, 2), (2, 2), (1, 3), (4,)]
每个序列的计算都基于它之前的序列。这是它如何工作的逐行解释:
if n == 0:
yield ()
return
生成器的前几行用于基本情况,其中n0。该return语句在产生一个空元组后使其退出,而不是继续执行其余代码。
for p in gen(n-1):
这是函数的递归步骤。由于gen是一个生成器,它是通过循环完成的,因此其余代码将重复运行,并p从递归结果中获取不同的值。
yield (1, ) + p
这是一条重要的线。这样做是从生成器中产生一个新的元组,该元组通过将 a 连接1到 value 的前面而形成p。如果p是空元组,这将是一个 1 元组。如果p已经有一些值,则元组将变得更长。
if p and (len(p)<2 or p[1] > p[0]):
这是一个复杂的条件。第二次检查,len(p)<2真的可以写len(p)==1,因为p and开头的部分已经排除了p为空。
p为了通过,可以有两种值。要么p只有一个值,要么有更多的值,并且它的第一个值小于它的第二个值。所以(3,)会过去,也会过去,(1,2)但(1,1,1)不会。
yield (p[0] + 1, ) + p[1:]
这是另一个元组连接。它将第一个值增加p一个,然后将其与其余部分连接起来p(使用切片)。So (3,)will become (4,)(切片为空), (1,2)become(2,2)等。
所以,现在让我们来看看我们上面看到的结果。
[()]
等于 0 时,基本情况被击中,你会得到n一个空元组。
[(1,)]
等于 1 时,n循环运行一次,并连接(1,)到空元组,产生 1-tuple (1,)。条件的第一部分if没有通过,因为p它是空的。
[(1,1), (2,)]
n等于 2,循环仍然只运行一次。首先,它(1,1)通过(1,)与自身连接得到 get (1,1)。但是这一次,该if块运行,因为p只有一个值。所以它也产生了(p[0]+1,) + p[1:]。其中的切片部分是空元组,但第一部分是(2,).
[(1,1,1), (1,2), (3,)]
现在终于循环运行不止一次了!虽然第一次,它将另一个连接(1,)到前面(1,1),并且由于if不满足语句的条件,这就是它所做的一切。然而,在第二次通过时,它会产生两个值,一次连接(1,),(2,)然后递增(2,)到(3,).
[(1, 1, 1, 1), (1, 1, 2), (2, 2), (1, 3), (4,)]
这个我会留给你,让你走过。与上述情况一样,p它将采用上一级调用 ( (1,1,1), (1,2), (3,)) 中的每个值,并且对于每个值,它将产生一个或两个新结果。