Python 如何将iterable转换为流?

Python 如何将iterable转换为流?,python,stream,iterator,Python,Stream,Iterator,如果我有一个包含字符串的iterable,有没有简单的方法将其转换为流?我想这样做: def make_file(): yield "hello\n" yield "world\n" output = tarfile.TarFile(…) stream = iterable_to_stream(make_file()) output.addfile(…, stream) 起点: class iterable_to_stream: def __init__(self,

如果我有一个包含字符串的iterable,有没有简单的方法将其转换为流?我想这样做:

def make_file():
    yield "hello\n"
    yield "world\n"

output = tarfile.TarFile(…)
stream = iterable_to_stream(make_file())
output.addfile(…, stream)
起点:

class iterable_to_stream:
    def __init__(self, iterable):
        self.iter = iter(iterable)

    def read(self):
        try:
            return self.iter.next()
        except StopIteration:
            return ""

TarFile接受提供的任何内容--因此您可以使用(如果您使用的是Python 3.X)生成所需的内容
TarFile.addfile()
,或者您可以创建自己的类来提供并生成所需的内容。

因为看起来没有“标准”的方法,我已经完成了一个简单的实现:

class iter_to_stream(object):
    def __init__(self, iterable):
        self.buffered = ""
        self.iter = iter(iterable)

    def read(self, size):
        result = ""
        while size > 0:
            data = self.buffered or next(self.iter, None)
            self.buffered = ""
            if data is None:
                break
            size -= len(data)
            if size < 0:
                data, self.buffered = data[:size], data[size:]
            result += data
        return result
类iter\u到\u流(对象):
定义初始值(自,可编辑):
self.buffered=“”
self.iter=iter(iterable)
def读取(自身,大小):
result=“”
当大小>0时:
数据=self.buffered或next(self.iter,无)
self.buffered=“”
如果数据为无:
打破
大小-=长度(数据)
如果尺寸小于0:
数据,自缓冲=数据[:大小],数据[大小:]
结果+=数据
返回结果

这是我的流式迭代器,它是通过iterables支持流式分块请求的一个实验分支:

class IterStreamer(object):
    """
    File-like streaming iterator.
    """
    def __init__(self, generator):
        self.generator = generator
        self.iterator = iter(generator)
        self.leftover = ''

    def __len__(self):
        return self.generator.__len__()

    def __iter__(self):
        return self.iterator

    def next(self):
        return self.iterator.next()

    def read(self, size):
        data = self.leftover
        count = len(self.leftover)

        if count < size:
            try:
                while count < size:
                    chunk = self.next()
                    data += chunk
                    count += len(chunk)
            except StopIteration:
                pass

        self.leftover = data[size:]

        return data[:size]
类IterStreamer(对象):
"""
类文件流迭代器。
"""
定义初始化(自身,生成器):
self.generator=generator
self.iterator=iter(生成器)
self.leftover=“”
定义(自我):
返回自生成器。\uuuu len\uuuuu()
定义(自我):
返回自迭代器
def next(自我):
返回self.iterator.next()
def读取(自身,大小):
数据=自身剩余
计数=长度(自身剩余)
如果计数<大小:
尝试:
当计数<大小时:
chunk=self.next()
数据+=块
计数+=len(块)
除停止迭代外:
通过
self.leftover=数据[大小:]
返回数据[:大小]
具有上下文的源:

相关单元测试:

>这段代码并没有使它进入稳定的分支,但是由于没有大量的块请求得到了很好的支持,但是它应该是你要做的一个很好的基础。有关如何使用它的示例,请参见源链接

Python3已经()了,取代了旧的类似文件的对象协议。(模块中的Python2中也提供了新的API,它与类文件对象协议向后兼容。)

,在Python 2和3中:

import io

def iterable_to_stream(iterable, buffer_size=io.DEFAULT_BUFFER_SIZE):
    """
    Lets you use an iterable (e.g. a generator) that yields bytestrings as a read-only
    input stream.

    The stream implements Python 3's newer I/O API (available in Python 2's io module).
    For efficiency, the stream is buffered.
    """
    class IterStream(io.RawIOBase):
        def __init__(self):
            self.leftover = None
        def readable(self):
            return True
        def readinto(self, b):
            try:
                l = len(b)  # We're supposed to return at most this much
                chunk = self.leftover or next(iterable)
                output, self.leftover = chunk[:l], chunk[l:]
                b[:len(output)] = output
                return len(output)
            except StopIteration:
                return 0    # indicate EOF
    return io.BufferedReader(IterStream(), buffer_size=buffer_size)
用法示例:

with iterable_to_stream(str(x**2).encode('utf8') for x in range(11)) as s:
    print(s.read())

对一位伟人的答案稍加修改。这里,
readinto(b)
实现对底层迭代器进行多次调用,以便根据给定的可写字节大小(如object
b
)收集尽可能多的字节

类迭代器Reader(io.RawIOBase): 定义初始化(self,迭代器): self.iterator=迭代器 self.leftover=[] def readinto(自身,缓冲区:bytearray)->可选[int]: 大小=长度(缓冲区) 而len(自身剩余)<尺寸: 尝试: self.leftover.extend(下一步(self.iterator)) 除停止迭代外: 打破 如果len(自身剩余)==0: 返回0 输出,self.leftover=self.leftover[:size],self.leftover[size:] 缓冲器[:len(输出)]=输出 返回透镜(输出) def可读(自)->bool: 返回真值 使用方法:

def iterator1():
    for i in ('a', 'b', 'c', 'd', 'e', 'f', 'g'):
        res = i * 3
        yield res.encode("utf8")


iterreader = IteratorReader(iterator1())
while True:
    r = iterreader.read(4)
    if not r:
        break
    print(r)

我对streams不太了解,但是你想要
stream=io.StringIO(“.”.join(make_file())
?不,我不想要
make_file()
可能会返回一个大文件,我不想将其加载到内存中。有趣的链接:@TokenMacGuy:对不起,我不认为我看到了该链接的重要性…对-但是有没有办法通过StringIO流式传输迭代器?在将输入文件写入StringIO之前,我不希望将整个输入文件加载到内存中。@David——据我所知,不是这样。我想给你一个例子,围绕着
StringIO
包装一个类,但看起来你已经得到了你所需要的:-)嗯…而这肯定会自行爆炸(如果
next(iter)
返回
?如果有人胆敢将一个大小传递给
read(…)
)…我想我可以用一个
BufferedReader
来处理这些细节…对不起,伙计,这似乎不可行。BufferedReader需要一个
RawIOBase
的实例,而这离实现该接口还差得远……而且它甚至没有实现基本的流API(例如,
read()
不接受大小)@David Wolever:似乎为您的iterable编写一个类似于
RawIOBase
的包装器并将其传递给
BufferReader
是可行的
RawIOBase
对象只有4个方法,您可能只需要实现3个
read…()
方法就可以了。这有一个bug,它将永远继续发出最后一个剩余的数据位。将
pass
替换为
return data
,bug就消失了。否。将
pass
换成
self.leftover='';返回数据
错误就消失了。修复了你们提到的错误。很抱歉没有响应,很长时间没有注意到Stackoverflow的通知。:)
read
仍然有一个陈旧的遗留错误,通过2020年的差异和Python 3.8中的更改得到修复,这仍然是最好的方法吗?试过了,它仍然有效,但也许可以简化?