我正在尝试压平嵌套的生成器生成器,但是我得到了意想不到的结果:
>>> 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)
更优雅。感谢投入。
令人遗憾的是,我只能将两个同样好的答案之一标记为正确。
答案 0 :(得分:7)
您可以使用chain.from_iterable
:
chain(*g)
>>> 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]
>>> g = ((3*i + j for j in range(3)) for i in range(3))
>>> list(itertools.chain.from_iterable(g))
[0, 1, 2, 3, 4, 5, 6, 7, 8]
答案 1 :(得分:4)
这个怎么样:
[x for y in g for x in y]
哪个收益率:
[0, 1, 2, 3, 4, 5, 6, 7, 8]
答案 2 :(得分:2)
猜猜你已经有了答案,但这是另一个观点。
问题在于,当创建每个内部生成器时,值生成表达式在外部变量i
上关闭,因此即使第一个内部生成器开始生成值,它也会使用&# 34;电流"值i
。如果外部生成器已被完全使用,那么它将具有值i=2
(并且在chain(*g)
调用中的参数被评估之后,恰好就是chain
实际上是g = ((3*i1 + j for i1 in [i] for j in range(3)) for i in range(3))
之前的情况叫)。
以下狡猾的技巧将解决这个问题:
i
请注意,这些内部生成器不会在for
上关闭,因为[i]
子句是在生成器创建时计算的,因此单例列表i
评估及其价值"冻结"面对from_iterable
的价值的进一步变化。
这种方法优于chain.from_iterable
答案,如果你想在g = ((3*i1 + j for i1 in [i] for j in range(3)) for i in range(3))
g1 = next(g)
g2 = next(g)
g3 = next(g)
电话之外使用它,它会更加通用 - 它总会产生"正确"内部发电机,无论外部发电机在使用内部发电机之前是部分还是完全消耗。例如,在以下代码中:
list(g1)
list(g2)
list(g3)
你可以插入这些行:
public abstract class ValueObject<T> where T : ValueObject<T>
{
protected abstract IEnumerable<object> GetAttributesToIncludeInEqualityCheck();
public override bool Equals(object other)
{
return Equals(other as T);
}
public bool Equals(T other)
{
if (other == null)
{
return false;
}
return GetAttributesToIncludeInEqualityCheck()
.SequenceEqual(other.GetAttributesToIncludeInEqualityCheck());
}
public static bool operator ==(ValueObject<T> left, ValueObject<T> right)
{
return Equals(left, right);
}
public static bool operator !=(ValueObject<T> left, ValueObject<T> right)
{
return !(left == right);
}
public override int GetHashCode()
{
int hash = 17;
foreach (var obj in this.GetAttributesToIncludeInEqualityCheck())
hash = hash * 31 + (obj == null ? 0 : obj.GetHashCode());
return hash;
}
}
在定义相应内部生成器之后的任何时间点的任何顺序,您将得到正确的结果。