groupby 迭代器返回分组函数结果的元组和一个新迭代器,该迭代器与groupby 运算符正在处理的同一“外部”迭代器相关联。当您将dict() 应用于groupby 返回的迭代器而不使用此“内部”迭代器时,groupby 将不得不为您推进“外部”迭代器。您必须意识到groupby 函数不会作用于序列,它会将任何此类序列转换为您的迭代器。
也许用一些比喻和挥手更好地解释这一点。请跟随我们形成一条桶线。
将迭代器想象为一个从井中用桶装水的人。他有无限数量的桶可以使用,但井可能是有限的。每次你向这个人要一桶水,他都会从水井里拿出一桶新水递给你。
在groupby 案例中,您将另一个人插入到您萌芽的桶链中。这个人根本不会立即传递水桶。每次您请求存储桶时,他都会将您给出的指令的结果以及 另一个 人传递给您,然后他会通过 groupby 人将您的存储桶传递给正在请求的任何人,只要它们匹配指令的结果相同。如果指令的结果发生变化,groupby 桶传递者将停止传递这些桶。所以well 给了groupby 桶,groupby 将它传递给每个组的人,group A,group B,等等。
在您的示例中,水是有编号的,但只能从井中抽取 1000 个水桶。当您将 groupby 人传递给 dict() 调用时,会发生以下情况:
您的dict() 呼叫要求groupby 提供存储桶。现在,groupby 向井边的人要一个水桶,记住了指示的结果,抓住水桶。对于dict(),他将传递指令的结果 (False) 和一个新人 group A。结果存储为key,想要拉桶的group A人存储为value。然而,这个人还没有要水桶,因为没有人要它。
您的dict() 呼叫要求groupby 提供另一个存储桶。 groupby 有这些说明,然后去寻找结果发生变化的下一个桶。它仍然抓着第一个桶,没有人要它,所以它扔掉这个桶。相反,它要求从井中取出下一个桶并使用他的指示。结果和以前一样,所以它也把这个新桶扔掉了!更多的水流过地板,接下来的 499 个水桶也是如此。只有当编号为 501 的桶被传递时结果才会改变,所以现在groupby 找到另一个人来指示(人group B),连同新的结果True,将这两个传递给@987654350 @。
您的 dict() 调用将 True 存储为键,将人员 group B 存储为值。 group B 什么都不做,没人要水。
您的dict() 请求另一个存储桶。 groupby 溢出更多的水,直到它拿着编号为 999 的桶,井边的人耸了耸肩,说现在井是空的。 groupby 告诉dict() 井是空的,没有更多的桶来了,请他不要再问了。它仍然拿着编号为 999 的桶,因为它永远不必为井中的下一个桶腾出空间。
现在你来,向dict() 询问与键True 相关联的事物,即人group B。您将group B 传递给list(),因此它将向group B 询问group B 可以获得的所有桶。 group B 回到 groupby,他只持有一个桶,编号为 999 的桶,该桶的指令结果与 group B 正在寻找的内容相匹配。所以这一桶group B给了list(),然后耸了耸肩,因为已经没有桶了,因为groupby告诉他的。
然后您向 dict() 询问与密钥 False 关联的人员,即人员 group A。到现在为止,groupby 已经没有什么可提供的了,井干了,他正站在一个有 999 桶水的水坑里,水里到处都是数字。你的第二个list() 什么也得不到。
这个故事的寓意是什么?在与groupby 交谈时立即要求所有桶装水,因为如果你不这样做,他会把它们全部洒出来!迭代器就像幻想曲中的扫帚,在不知不觉中勤奋地移动水,如果你不知道如何控制它们,你最好希望自己没有水。
这里的代码可以满足你的期望(用少一点的水来防止洪水):
>>> from itertools import groupby
>>> keyfunc = lambda x : x > 5
>>> obj = dict((k, list(v)) for k, v in groupby(range(10), keyfunc))
>>> obj(True)
[0, 1, 2, 3, 4, 5]
>>> obj(False)
[6, 7, 8, 9]