为了理解发生了什么,它有助于打印生成器的输出,以获得几个顺序增加的值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
生成器的前几行用于基本情况,其中n
0。该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,)
) 中的每个值,并且对于每个值,它将产生一个或两个新结果。