我想链接多个iterables,所有内容都是懒惰的评估(速度至关重要),要做到以下几点:
真实的例子比较复杂,这是一个简化的例子:
这是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)
的结果 - 这有点挫败了目的?
是否更快地手动定义我的发电机?
答案 0 :(得分:2)
显式("手动")生成器表达式应优先于使用map
和filter
。它对大多数人来说更具可读性,而且更灵活。
如果我理解你的问题,这个生成器表达式可以满足您的需求:
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]