为什么链接迭代这么复杂?简化此代码

时间:2014-12-12 12:39:41

标签: python-3.x generator itertools chaining iterable

我想链接多个iterables,所有内容都是懒惰的评估(速度至关重要),要做到以下几点:

  • 从一个庞大的stdin行读取许多整数
  • split()那一行
  • 将生成的字符串转换为int
  • 计算连续整数之间的差异
  • ......以及此处未显示的其他一些内容

真实的例子比较复杂,这是一个简化的例子:

这是stdin的示例行: 2 13 4 16 16 15 22 17 8 8 7 6

(出于调试目的,下面的instream可能指向sys.stdin或打开的文件句柄

你不能简单地链接生成器,因为map()返回一个(懒惰评估的)列表:

import itertools
gen1 = map(int, (map(str.split, instream))) # CAN'T CHAIN DIRECTLY

我找到的最简单的工作解决方案是,它肯定不能简化吗?

gen1 = map(int, itertools.chain.from_iterable(itertools.chain(map(str.split, instream))))

为什么我需要链接itertools.chain.from_iterable(itertools.chain只是为了处理来自map(str.split, instream)的结果 - 这有点挫败了目的? 是否更快地手动定义我的发电机?

2 个答案:

答案 0 :(得分:2)

显式("手动")生成器表达式应优先于使用mapfilter。它对大多数人来说更具可读性,而且更灵活。

如果我理解你的问题,这个生成器表达式可以满足您的需求:

gen1 = ( int(x) for line in instream for x in line.split() )

答案 1 :(得分:0)

您可以手动构建您的发电机:

import string

def gen1(stream):
    # presuming that stream is of type io.TextIOBase


    s = ""
    c = stream.read(1)  
    while len(c)>0:

        if (c not in string.digits):
            if len(s) > 0:
                i = int(s)
                yield i
                s = ""
        else:
            s += c

        c = stream.read(1)

    if len(s) > 0:
        i = int(s)
        yield i 


import io
g = gen1(io.StringIO("12 45  6 7 88"))
for x in g:    # dangerous if stream is unlimited
    print(x)

这当然不是最漂亮的代码,但它可以满足您的需求。 说明:

如果您的输入无限长,则必须以块(或明智的方式)读取它。 每当遇到非数字(空白)时,您将已读取的字符转换为整数并生成它。 您还必须考虑到达EOF时会发生什么。 我的实现可能不是很好,因为我正在阅读char-wise。使用块可以显着加快速度。

编辑至于为什么您的方法永远不会有效:

map(str.split, instream)

根本不会做你认为它做的事情。 map将给定函数str.split应用于作为第二个参数给出的迭代器的每个元素。在你的情况下,它是一个流,即一个文件对象,在sys.stdin的情况下,特别是一个io.TextIOBase对象。哪些确实可以迭代。逐行,这显然不是你想要的!实际上,您逐行迭代输入并将每行拆分为单词。地图生成器迭代(很多)单词列表,而不是单词列表。这就是为什么你必须将它们链接在一起以获得单个列表来迭代。

此外,itertools.chain()中的itertools.chain.from_iterable(itertools.chain(map(...)))也是多余的。 itertools.chain将其参数(每个都是不可更改的对象)链接在一起成为一个迭代器。你只给它一个参数,所以没有什么可以链在一起,它基本上返回地图对象不变。 另一方面,itertools.chain.from_iterable()接受一个参数,它应该是迭代器的迭代器(例如列表列表)并将其展平为一个迭代器(列表)。

<强> EDIT2

import io, itertools

instream = io.StringIO("12 45 \n 66 7 88")
gen1 = itertools.chain.from_iterable(map(str.split, instream))
gen2 = map(int, gen1)
list(gen2)

返回

[12, 45, 66, 7, 88]