生成器的Python初始化

时间:2018-09-28 00:36:55

标签: python generator

我正在尝试一种通用的方式来生成多个范围或列表的所有组合,例如

[range(0, 2), range(2, 5), range(4, 6), range(2, 3)]

应返回2x3x2x1 = 12个元素列表。

[[0, 2, 4, 2],
 [0, 2, 5, 2],
 [0, 3, 4, 2],
 [0, 3, 5, 2],
 [0, 4, 4, 2],
 [0, 4, 5, 2],
 [1, 2, 4, 2],
 [1, 2, 5, 2],
 [1, 3, 4, 2],
 [1, 3, 5, 2],
 [1, 4, 4, 2],
 [1, 4, 5, 2]]

到目前为止,这里一切都很好。当我对它进行硬编码时,

x = [ ( [a,b] for a in rgs[0] for b in rgs[1] ) ]
x.append( ( a + [b] for a in x[-1] for b in rgs[2]) )
x.append( ( a + [b] for a in x[-1] for b in rgs[3]) )

我得到了很好的结果。但是,当我尝试将其概括时,

x = [ ( [a,b] for a in rgs[0] for b in rgs[1] ) ]
for i in range(1,len(rgs)-1):
    x.append( ( a + [b] for a in x[-1] for b in rgs[i+1]) )

我获得了一个由6个元素组成的列表:

[[0, 2, 2, 2],
 [0, 3, 2, 2],
 [0, 4, 2, 2],
 [1, 2, 2, 2],
 [1, 3, 2, 2],
 [1, 4, 2, 2]]

此外,我注意到的是,前两个之后生成的所有范围都使用rgs[-1]中的范围而不是正确的范围。我难以理解为什么会这样,因为我相信那两个代码示例是相同的,除了后者是任意大量范围的更通用形式。

2 个答案:

答案 0 :(得分:2)

您可以使用itertools.product输出元组列表

输入:

import itertools

a= [range(0, 2), range(2, 5), range(4, 6), range(2, 3)]
list(itertools.product(*a))

输出:

[(0, 2, 4, 2),
 (0, 2, 5, 2),
 (0, 3, 4, 2),
 (0, 3, 5, 2),
 (0, 4, 4, 2),
 (0, 4, 5, 2),
 (1, 2, 4, 2),
 (1, 2, 5, 2),
 (1, 3, 4, 2),
 (1, 3, 5, 2),
 (1, 4, 4, 2),
 (1, 4, 5, 2)]

运行第一个代码时没有得到相同的结果。我必须对其进行一些更改:

x = [ ( [a,b] for a in rgs[0] for b in rgs[1] ) ]
x.append( ( a + [b] for a in x[-1] for b in rgs[2]) )
x =  list( a + [b] for a in x[-1] for b in rgs[3]) 

大多数不了解itertools的人都会这样做:

x=[]
for i0 in rgs[0]:
    for i1 in rgs[1]:
        for i2 in rgs[2]:
            for i3 in rgs[3]:
                x.append([i0,i1,i2,i3])

或者使用列表理解(不要这样做,看起来很乱):

[[i0,i1,i2,i3] for i3 in rgs[3] for i2 in rgs[2] for i1 in rgs[1] for i0 in rgs[0]]

答案 1 :(得分:1)

您的问题与在循环中创建生成器表达式有关。生成器表达式实现为函数,并且像函数一样,它们可以具有“自由”变量,可以在包含的命名空间中查找它们。您的生成器表达式正在从其定义之外访问i,因此,它们最终会看到您期望的其他i值。

下面是一个可能更容易理解的示例:

def gen()
   print(i)
   yield 10

x = []
for i in range(3):
    x.append(gen())  # add several generators while `i` has several different values

for g in x:
    list(g)   # consume the generators, so they can print `i`

在这里,我没有使用i值来做有用的事情,而是编写了一个生成器函数,将其打印出来。如果运行此代码,您将看到所有生成器都打印出2,因为这是最终运行时(在第一个循环结束之后)i的值。

您的情况会有些微妙,因为您在创建下一个生成器时会消耗上一个生成器,但是总体思路是相同的。您期望超过rgs[2]的生成器表达式循环实际上已超过rgs[3],因为实际上是在声明生成器表达式的时间之间以rgs[i+1]i进行查找以及何时食用。