我试图展平一个嵌套的生成器,但是我得到了一个意外的结果:
是否有一种方法可以展开任意深度的嵌套生成器表达式(也许使用除了 itertools.chain 之外的其他东西)?
编辑:
不,我的问题不是Variable Scope In Generators In Classes的重复。我老实说无法理解这两个问题如何相关。也许管理员可以解释一下为什么他认为这是一个重复的问题。
此外,对于我的问题,两个答案都是正确的,因为它们可以用来编写正确展开嵌套生成器的函数。
>>> g = ((3*i + j for j in range(3)) for i in range(3))
>>> list(itertools.chain(*g))
[6, 7, 8, 6, 7, 8, 6, 7, 8]
我希望结果看起来像这样:
[0, 1, 2, 3, 4, 5, 6, 7, 8]
我认为我得到意外的结果是因为内部生成器直到外部生成器已经被迭代过并设置i为2之后才被评估。我可以通过强制使用列表推导式而不是生成器表达式来评估内部生成器来拼凑出一个解决方案:
>>> g = ([3*i + j for j in range(3)] for i in range(3))
>>> list(itertools.chain(*g))
[0, 1, 2, 3, 4, 5, 6, 7, 8]
理想情况下,我希望有一个完全懒惰的解决方案,不会强制评估嵌套元素的内部直到它们被使用。是否有一种方法可以展开任意深度的嵌套生成器表达式(也许使用除了 itertools.chain 之外的其他东西)?
编辑:
不,我的问题不是Variable Scope In Generators In Classes的重复。我老实说无法理解这两个问题如何相关。也许管理员可以解释一下为什么他认为这是一个重复的问题。
此外,对于我的问题,两个答案都是正确的,因为它们可以用来编写正确展开嵌套生成器的函数。
def flattened1(iterable):
iter1, iter2 = itertools.tee(iterable)
if isinstance(next(iter1), collections.Iterable):
return flattened1(x for y in iter2 for x in y)
else:
return iter2
def flattened2(iterable):
iter1, iter2 = itertools.tee(iterable)
if isinstance(next(iter1), collections.Iterable):
return flattened2(itertools.chain.from_iterable(iter2))
else:
return iter2
就我使用 timeit
的观察,它们的性能表现是相同的。
>>> timeit(test1, setup1, number=1000000)
18.173431718023494
>>> timeit(test2, setup2, number=1000000)
17.854709611972794
就样式而言,我也不确定哪个更好,因为x for y in iter2 for x in y
有点晦涩难懂,但可以说比itertools.chain.from_iterable(iter2)
更优雅。欢迎提供意见。
遗憾的是,我只能标记其中一个同样好的答案为正确。
g
,重新设计内部生成器以避免不良变量捕获可能是个好主意。 - K. A. Buhrrange()
这样的生成器(range()
可以很好地生成扁平化的数字范围),而是让其他程序员能够使用生成器定义多维数组。如果他们认为他们的多维数组看起来像[[0,1],[2,3]],而实际上它看起来像[[2,3],[2,3]]——而且没有引发任何错误——那么我就搞砸了。 - castle-bravo