Как преобразовать итерируемое в поток?
Если у меня есть итерируемые содержащие строки, есть ли простой способ превратить его в поток? Я хочу сделать что-то вроде этого:
def make_file():
yield "hello\n"
yield "world\n"
output = tarfile.TarFile(…)
stream = iterable_to_stream(make_file())
output.addfile(…, stream)
Ответы
Ответ 1
Здесь мой потоковый итератор - экспериментальная ветвь urllib3, поддерживающая потоковый запрос с помощью 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]
Источник с контекстом:
https://github.com/shazow/urllib3/blob/filepost-stream/urllib3/filepost.py#L23
Связанные модульные тесты:
https://github.com/shazow/urllib3/blob/filepost-stream/test/test_filepost.py#L9
Увы, этот код еще не попал в стабильную ветвь, так как безупречные chunked-запросы плохо поддерживаются, но это должна быть хорошая основа для того, что вы пытаетесь сделать. См. Ссылку источника для примеров, показывающих, как ее можно использовать.
Ответ 2
Python 3 имеет новый API потока ввода-вывода (docs), заменив старый файл -подобный объектный протокол. (Новый API также доступен в Python 2 в модуле io
, и он обратно совместим с файловым протоколом объектов.)
Здесь реализована реализация для нового 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 newer I/O API (available in Python 2 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())
Ответ 3
Так как это не похоже на "стандартный" способ сделать это, я ударил простую реализацию:
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
Ответ 4
Начальная точка:
class iterable_to_stream:
def __init__(self, iterable):
self.iter = iter(iterable)
def read(self):
try:
return self.iter.next()
except StopIteration:
return ""
Ответ 5
TarFile берет все, что обеспечивает файловый интерфейс - чтобы вы могли либо использовать StringIO
(io.StringIO
, если вы используете Python 3.X), чтобы получить то, что вам нужно TarFile.addfile()
или вы можете создать свой собственный класс, который предоставляет файловый интерфейс и дает то, что вам нужно.