python中的生成器如何工作?

时间:2016-01-18 02:20:03

标签: python

假设我有(名称,分数)形式的100个元组。我希望将9个组中的100个元组组合在一起,总组得分高于30.很好,我使用itertools.combinations(迭代,9)。问题是当我想在for循环中经历这些组合时需要“很长时间”,因为有很多组合(100!/ 91!/ 9!组合)。这是迄今为止的代码:

import time
import itertools
import csv

start_time=time.clock()

with open('data.csv') as f:
    data=[tuple(line) for line in csv.reader(f)]

x = itertools.combinations(data,9)
count = 0
for i in x:
    count = count +1
print count
print("--- %s seconds ---" % (time.clock() - start_time))

好吧我想通过约束组合来限制这些组合,以便组的总得分大于30.为此,我使用了itertools.combinations中的等效代码并使用了这样的约束函数:

import time
import csv

start_time=time.clock()

with open('data.csv') as f:
    data=[tuple(line) for line in csv.reader(f)]

def pconstraint(combo):
    totalscore=0
    for player in combo:
        totalscore += int(player[1])
    if totalscore > 30:
        return True

def combinations(iterable, r):
    # combinations('ABCD', 2) --> AB AC AD BC BD CD
    # combinations(range(4), 3) --> 012 013 023 123
    pool = tuple(iterable)
    n = len(pool)
    if r > n:
        return
    indices = range(r)
    y = tuple(pool[i] for i in indices)
    if pconstraint(y):
        yield y

    while True:
        for i in reversed(range(r)):
            if indices[i] != i + n - r:
                break
        else:
            return
        indices[i] += 1
        for j in range(i+1, r):
            indices[j] = indices[j-1] + 1

        x = tuple(pool[i] for i in indices)
        if pconstraint(x):
            yield x

count = 0
for i in x:
    count = count +1
print count
print("--- %s seconds ---" % (time.clock() - start_time))

比方说,例如,当我们得分大于30的约束时,我们从(100!/ 91!/ 9!组合)变为10。使用itertools完成所有组合所需的时间.combinations(data,9)比使用约束快得多。另外,当我使用组合函数并且在没有约束变化的情况下单独运行它时,THAT也比使用约束运行得更快。我的想法是,如果发电机产生或产量较少的东西,它应该更快通过它?但显然我的想法是错的。我该怎么做才能尽快显示我想要的数据(9个元组的10个组合,得分大于30)?

2 个答案:

答案 0 :(得分:0)

无法在itertools.combinations中指定谓词。但是,您可以使用genex对生成器产生的结果强制执行谓词。

>>> for n in (x for x in itertools.combinations([1, 2, 3, 4], 3) if sum(x) > 7):
...   print n
... 
(1, 3, 4)
(2, 3, 4)

答案 1 :(得分:0)

我认为,鉴于发电机的工作原理,您所看到的内容是有道理的。对组合本身的呼吁: x = itertools.combinations(data,9) 期望使用生成器对象返回(即在执行while中的combinations循环和for中的constraint循环之前)

将花费最多时间的代码是"解开"发电机:

for i in x:
    count = count +1

通过yield的魔法实际执行循环时的情况。所以你添加了一个约束,它应该有效地减少了产量,但是解释器仍然会在while语句中执行相同数量的迭代乘以for语句pconstraint语句中的9次迭代当你迭代生成器x时它会这样做。

考虑yield的最简单方法是一种goto语句,它在迭代生成器对象的代码和执行屈服的行之间来回跳转。每次从生成器请求生成的项时,执行将在最后生成的行中的combinations内继续执行,直到它到达下一个yield,然后返回一个新项并等待(yield)以请求另一个生成的项x.next()中隐含的for i in x以及当它再次恢复执行时超过最后yield等等。

希望有所帮助。