Python 在列表中创建多个生成器

Python 在列表中创建多个生成器,python,generator,list-comprehension,Python,Generator,List Comprehension,我试图将同一套(颜色)的卡片分组,并在生成器中排名,然后将这些生成器存储在列表中 我提出的解决方案就是这样做的,除了所有生成器都包含完全相同的卡片这一事实。知道为什么吗 这是密码 deck=range(52) gens=[(i for i in deck if i%13==v) for v in range(13)] 基于此,我期望例如: gens[1].next() 1 gens[1].next() 14 gens[10].next() 10 gens[10].next() 23 但

我试图将同一套(颜色)的卡片分组,并在生成器中排名,然后将这些生成器存储在列表中

我提出的解决方案就是这样做的,除了所有生成器都包含完全相同的卡片这一事实。知道为什么吗

这是密码

deck=range(52)

gens=[(i for i in deck if i%13==v) for v in range(13)]
基于此,我期望例如:

gens[1].next()
1
gens[1].next()
14


gens[10].next()
10
gens[10].next()
23
但是我得到了

gens[1].next()
12

gens[1].next()
25

gens[1].next()
38

列表中的所有生成器都返回相同的结果。

问题在于生成器表达式中的名称
v
引用了列表中的变量
v
。因此,当生成器表达式的代码实际运行时(当调用
next
时),它会查看变量
v
,并查看值
12
,无论创建生成器时
v
的值是多少

一个解决方法:

deck = range(52)

def select_kth(v):
    return (i for i in deck if i % 13 == v)

gens = [select_kth(v) for v in range(13)]
因为我们定义了一个函数,所以名称
v
可以在它自己的命名环境中使用,因此不会被修改

如果您真的愿意,可以用一行代码来完成:

 gens = [(lambda v: (i for i in deck if i % 13 == v))(v) for v in range(13)]

如果将其转换为等效的嵌套循环,则可以更容易地看到作用域问题:

gens = []
for v in range(13):
    def gen():
        for i in deck:
            if i%13 == v:
                yield i
    gens.append(gen())
最后,13个生成器都绑定到相同的
v
,12

因此,这里的解决方案与任何其他作用域问题中的解决方案相同:您需要创建一个包含
v
的新作用域。最简单的方法是创建一个新函数:

gens = [(lambda x: (i for i in deck if i%13==x)(v) for v in range(13)]

我本来打算删除这个,因为它和Dougal的答案是一样的,他比我早一分钟到这里,但是…它有一些投票,所以我想人们认为这是值得保留的…或者Dougal应该复制到嵌套循环的转换,并在一个答案中同时拥有两个世界的优点?你的答案是一种完全不同的解释方式。我会留下它。我喜欢你的解释,认为它值得自己保留,而不是我偷它,不需要删除。:)