解决发电机中评估时间差异的问题

时间:2017-12-13 10:02:41

标签: python generator

我发现自己在评估时间不一致的情况下遇到了问题'今天来自this list,我正在努力解决它。

作为我的问题的简短演示,我创建了无限生成器,跳过每个n个数字,n来自[2..5]

from itertools import count

skip_lists = []
for idx in range(2, 5):
    # skip every 2nd, 3rd, 4th.. number
    skip_lists.append(x for x in count() if (x % idx) != 0)

# print first 10 numbers of every skip_list
for skip_list in skip_lists:
    for _, num in zip(range(10), skip_list):
        print("{}, ".format(num), end="")
    print()

预期产出:

1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 
1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 
1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 

实际输出:

1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 
1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 
1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 

一旦我记得这个伟大的功能,我试图解决"它通过将if子句变量绑定到一个常量,该常量将成为skip_list的一部分:

from itertools import count

skip_lists = []
for idx in range(2, 5):
    # bind the skip distance
    skip_lists.append([idx])
    # same as in the first try, but use bound value instead of 'idx' 
    skip_lists[-1].append(x for x in count() if (x % skip_lists[-1][0]) != 0)

# print first 10 numbers of every skip_list
for skip_list in (entry[1] for entry in skip_lists):
    for _, num in zip(range(10), skip_list):
        print("{}, ".format(num), end="")
    print()

但是又一次:

1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 
1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 
1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 

除了实际解决方案之外,我还想了解为什么我的黑客没有工作。

1 个答案:

答案 0 :(得分:3)

在开始迭代生成器(懒惰地评估生成器)之前,永远不会查找idx的值,此时idx = 4最新的iteratee值是模块范围中存在的值

您可以通过将idx传递给函数并在每个生成器的评估时间从函数范围读取值,使idx中的每个附加生成器都有状态。这利用了在gen处评估生成器表达式的可迭代源的事实。 exp的创建时间,因此在循环的每次迭代中调用该函数,idx 安全地存储在函数范围内:

from itertools import count

skip_lists = []

def skip_count(skip):
  return (x for x in count() if (x % skip) != 0)


for idx in range(2, 5):
    # skip every 2nd, 3rd, 4th.. number
    skip_lists.append(skip_count(idx))

生成器表达式在gen的可迭代源评估的图示。 exp的创作:

>>> (i for i in 5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable

你的情况有点棘手,因为排除实际上是在过滤器中完成的,而这个过滤器在创建时没有得到评估:

>>> (i for i in range(2) if i in 5)
<generator object <genexpr> at 0x109a0da50>

for 循环和过滤器都需要移动到存储idx的范围的原因越多;不只是过滤器。

另外,您可以使用itertools.islice代替您用来打印生成器表达式的 slice 的低效逻辑:

from itertools import islice

for skip_list in skip_lists:
    for num in islice(skip_list, 10):
        print("{}, ".format(num), end="")
    print()