【问题标题】:Why does zip eagerly evaluate the elements of its arguments?为什么 zip 急切地评估其论点的要素?
【发布时间】:2020-09-05 01:25:10
【问题描述】:

使用以下定义:

def a(x):
    return x + 1

def b(x):
    return x + 2

[map(a, range(3)), map(b, range(3)] 产生[<iterator>, <iterator>]。我正在尝试复制此功能,而不必两次构造 range 迭代器。

我做了这个功能:

def map_many(mappers, values):
    def get_many(value):
        for mapper in mappers:
            yield mapper(value)

    mapped_values = map(get_many, values)
    return zip(*mapped_values)

在做:

for my_iterator in map_many((a, b), range(3)):
    print(my_iterator)

结果:

(1, 2, 3)
(2, 3, 4)

为什么my_iterator 被热切评估?我本来希望它仍然是一个会被延迟评估的迭代器(当打印时,它会显示为类似于<iterator> 的东西)。据我了解,mapped_values 之前的 * 解包 mapped_values 本身(它是迭代器的迭代器),而不是解包将通过不断调用 next(mapped_values) 返回的所有迭代器。

是否可以让每个 my_iterator 保留为迭代器并延迟评估,而不是转换为 tuple

【问题讨论】:

  • 在 Python 2 中,您可能正在寻找 izip 而不是 zip。在 Python 3 中这应该不是问题。
  • 我不确定我是否理解您的功能背后的动机。您可以在 range 对象上进行任意多次迭代,这非常有效。做几乎任何替代方案都会变得更糟。您的实际用例是否尝试使用不能多次使用的迭代器来代替 range

标签: python python-3.x generator iterable-unpacking


【解决方案1】:

根据zip的文档,相当于:

def zip(*iterables):
    # zip('ABCD', 'xy') --> Ax By
    sentinel = object()
    iterators = [iter(it) for it in iterables]
    while iterators:
        result = []
        for it in iterators:
            elem = next(it, sentinel)
            if elem is sentinel:
                return
            result.append(elem)
        yield tuple(result)

如您所见,它确实评估了给定的每个迭代器,并在每个迭代器上调用next,直到其中至少一个被耗尽。从这个角度来看,您看到的是预期的行为。

【讨论】:

    猜你喜欢
    • 2013-08-16
    • 2021-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-12
    • 2018-05-13
    • 1970-01-01
    • 2013-06-02
    相关资源
    最近更新 更多