io.BytesIO - это "файл в памяти" для байтов. Очень удобный кирпичик.
Ниже набор реально полезных паттернов для io.BytesIO.
Когда нужно что-то, что ожидает "файлоподобный" объект (read/write/seek), но писать на диск не хочется.
from io import BytesIO buf = BytesIO() buf.write(b"hello ") buf.write(b"world") buf.seek(0) # как у файла data = buf.read() # b'hello world'
Типичный случай: библиотека принимает "файл", а вы можете дать ей BytesIO.
Когда данные уже есть в виде bytes, а хочется использовать API, которое работает с файлом (чтение по кускам, построчно, т.п.).
from io import BytesIO data = b"line1\nline2\nline3\n" f = BytesIO(data) for line in f: print(line) # b'line1\n' и т.д.
Паттерн: оборачиваем любой bytes / буфер в BytesIO и работаем как с файлом.
Когда нужно сложить много кусочков без постоянных конкатенаций a + b + c:
from io import BytesIO buf = BytesIO() for i in range(3): buf.write(f"chunk{i};".encode("utf-8")) result = buf.getvalue() # b'chunk0;chunk1;chunk2;'
BytesIO под капотом делает это эффективно.
Отличный паттерн для юнит-тестов: вместо реального файла - BytesIO.
from io import BytesIO def process_file(f): data = f.read() return data.upper() def test_process_file(): fake = BytesIO(b"abc") assert process_file(fake) == b"ABC"
Плюсы:
seek/tell), ошибки и т.д.BytesIO + zipfile, tarfile, PIL, requests и др.Очень распространённый паттерн: архив / изображение / что-то ещё в памяти.
import io import zipfile buf = io.BytesIO() with zipfile.ZipFile(buf, mode="w", compression=zipfile.ZIP_DEFLATED) as z: z.writestr("hello.txt", "привет") z.writestr("data.bin", b"\x00\x01\x02") zip_bytes = buf.getvalue() # готовый .zip как bytes
zip_bytes = ... # пришло по сети, из БД и т.п. buf = io.BytesIO(zip_bytes) with zipfile.ZipFile(buf, "r") as z: print(z.namelist())
Например, вы прочитали большой кусок из сокета, а хотите его "распарсить" как поток.
from io import BytesIO def parse_message(data: bytes): f = BytesIO(data) header = f.read(4) body_len = int.from_bytes(header, "big") body = f.read(body_len) return header, body
Паттерн: BytesIO как удобный "курсором по байтам".
Много библиотек умеют принимать "файл" вместо пути:
PIL.Image.open(f)pdfminer, mutagen, soundfile, wave и т.п.from io import BytesIO from PIL import Image # пример img_bytes = ... # получили по сети f = BytesIO(img_bytes) img = Image.open(f)
Иногда надо:
from io import BytesIO buf = BytesIO() buf.write(b"some data") buf.seek(0) def upload_file(fobj): # какая-то функция, которая читает из файла print(fobj.read()) upload_file(buf) # b"some data"
Одна функция пишет бинарные данные в файл, другая - читает. Можно соединить их через BytesIO, не трогая диск.
from io import BytesIO def producer(f): f.write(b"from producer") def consumer(f): f.seek(0) return f.read().upper() buf = BytesIO() producer(buf) result = consumer(buf) print(result) # b"FROM PRODUCER"
tempfile.TemporaryFile или обычный файл.getvalue() возвращает копию (в CPython - иногда разделяет буфер, но логически считать копией).buf.seek(0); buf.truncate() чтобы "очистить" и переиспользовать.buf = BytesIO() buf.write(b"old") buf.seek(0) buf.truncate() buf.write(b"new") print(buf.getvalue()) # b"new"