比较Python中的迭代器

时间:2013-08-01 23:03:26

标签: python itertools

from __future__ import print_function

from functools import partial
from itertools import imap, product

identity = lambda x: x
identity.__repr__ = 'id'

map_identity = partial(map, identity)
map_identity.__repr__ = 'map'

imap_identity = partial(imap, identity)
imap_identity.__repr__ = 'imap'

prod_map = partial(product, repeat=1)
prod_map.__repr__ = 'prod'



list(map(
    lambda (f, g):
        print(
            "{f_repr:4}(range(10)) compare {g_repr:4}(range(10)):  "\
            "{{ less: {less:1}, equal: {equal:1}}}".format(
                f_repr=f.__repr__,
                g_repr=g.__repr__,
                less=f(range(16))<g(range(16)),
                equal=f(range(16))==g(range(16)),
            )
        ),
    product([identity, map_identity, imap_identity, prod_map], repeat=2)
))

返回

id  (range(10)) compare id  (range(10)):  { less: 0, equal: 1}
id  (range(10)) compare map (range(10)):  { less: 0, equal: 1}
id  (range(10)) compare imap(range(10)):  { less: 0, equal: 0}
id  (range(10)) compare prod(range(10)):  { less: 0, equal: 0}
map (range(10)) compare id  (range(10)):  { less: 0, equal: 1}
map (range(10)) compare map (range(10)):  { less: 0, equal: 1}
map (range(10)) compare imap(range(10)):  { less: 0, equal: 0}
map (range(10)) compare prod(range(10)):  { less: 0, equal: 0}
imap(range(10)) compare id  (range(10)):  { less: 1, equal: 0}
imap(range(10)) compare map (range(10)):  { less: 1, equal: 0}
imap(range(10)) compare imap(range(10)):  { less: 1, equal: 0}
imap(range(10)) compare prod(range(10)):  { less: 1, equal: 0}
prod(range(10)) compare id  (range(10)):  { less: 1, equal: 0}
prod(range(10)) compare map (range(10)):  { less: 1, equal: 0}
prod(range(10)) compare imap(range(10)):  { less: 0, equal: 0}
prod(range(10)) compare prod(range(10)):  { less: 1, equal: 0}

在比较时,迭代器imapprod_map与列表id / map的行为有何不同?

2 个答案:

答案 0 :(得分:6)

这里有几个不同的问题。


首先,2.x中map的结果不是迭代器而是列表。通过按字典顺序比较两个列表来比较它们的元素。

迭代器和许多其他迭代器也是如此(因为这样做会消耗迭代器,使它们变得无用)。因此,像Python中的大多数类型没有任何其他明显的方法来比较它们的相等性,大多数迭代器类型(包括生成器,以及来自itertools的特殊C实现的迭代器)比较id 。碰巧迭代相同序列的两个不同的迭代器通常不会相等。

当然,identity的结果也是一个列表,如果你传递一个列表。 range(16)是一个列表。因此,在两个函数都在(id, map)的情况下,结果将是相等的;如果任一函数在(imap, prod)中,结果将相等。


其次,对两个不相关类型的值使用<(或其他排序比较)可以获得一致的任意运行但任意顺序。问一个人为什么不是另一个人是没有意义的。它必须是一个或另一个;它无关紧要。 (如果你真的想知道细节,你可以阅读你选择的解释器的源代码.CPython的实现就像你想象的那样简单,愚蠢:不相关类型的值按类型的规范名称有效排序。 )


那么,如果你希望知道两个迭代器是否迭代相同的序列呢?

一般情况下,你不能。例如chain(0, count(1))count(0)清楚地迭代相同的序列......但是它将花费你无限的时间来计算出来。 (当然在这种情况下,有关chaincount如何工作和访问其内部的足够信息,您可以在有限的时间内完成 - 至少我希望如此,或者您仍然在验证该段前面的“清楚”一点。但是如果不解决暂停问题,你就无法做到这一点。)

在简单的情况下,您可以在每个上调用list,比较两个列表(然后使用列表而不是原始的,现在为空的迭代器)。在某些情况下(例如,你可以依赖它们 - 非常早 - 或相等 - 但很小,但你不能依赖它们,如果不同则很小),你可以使用tee和只迭代到第一个差异(然后使用tee d副本而不是原始迭代器),但这基本上是相同的想法。

但实际上,你可能想要的只是这个:

equal_values = (x1 == x2 for x1, x2 in izip(i1, i2))

那么你和其他任何迭代器都有完全相同的问题,你已经知道如何处理,对吧?例如,如果您确定它们不是无限的,并且您愿意使用迭代器来查明它们是否相等:

equal_sequences = all(equal_values)

Python 3.x几乎消除了第一个问题,因为它没有充分的理由阻止你构建大量的中间列表,而是鼓励你将事物保存在迭代器中,直到你需要迭代它们(通常一次,在结束)。特别是,range不返回list - 尽管它确实返回了与自身相等的内容 - 而map返回迭代器。因此,只有当两个函数都是id时,才能获得相等。

Python 3.x完全消除了第二个问题,只是不让你比较不相关类型的值。如果您尝试,您将获得TypeError。所以,你不能误导寻找一个值小于另一个值的不存在的原因。

答案 1 :(得分:0)

我怀疑你没有比较相同的事情,所以我决定手动完成它。这里发生了什么......

>>> import functools
>>> from itertools import imap, product
>>> identity = lambda x: x
>>> map_identity = functools.partial(map, identity)
>>> map_identity(range(16))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
>>> map_identity(range(16)) == [x for x in range(16)]
True

当然,这是有道理的。

>>> imap_identity = functools.partial(imap, identity)
>>> imap_identity(range(16))
<itertools.imap object at 0x10aeb6f50>

哦哦

>>> imap_identity(range(16)) < identity
False
>>> imap_identity(range(16)) > identity
True
是的,那就是这样做的。这实际上让我在一两天前绊倒了。我以为我正在使用一个函数,但我正在使用一个生成器。你当然可以做这样的事情:

>>> im = imap_identity(range(16))
>>> [x for x in im]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

imap很棒,但由于它是一个生成器,只在必要时评估其序列,你必须要求它明确地给你一些数字。使用您的代码,您最终无法做到这一点。您可以使用代码执行此操作:

less=[x for x in f(range(16))]<[x for x in g(range(16))],
equal=[x for x in f(range(16))]==[x for x in g(range(16))]

使用list()一样好。当然,正如abarnert所指出的那样,你似乎并没有要求如何让它们相互比较,所以我可能最后说这一点是徒劳的。