El módulo `io` en Python

El módulo `io` en Python

Este artículo explica el módulo io en Python.

Explicaremos el módulo io en Python con ejemplos prácticos.

YouTube Video

El módulo io en Python

El procesamiento de entrada/salida forma la base de todo tipo de operaciones de datos, como archivos, redes y E/S estándar. El módulo io de Python proporciona un conjunto de clases abstractas que unifican estas operaciones de entrada/salida. El concepto clave para entender este módulo es la idea de una "corriente" (stream).

¿Qué es un flujo?

Una corriente es un flujo abstracto para leer y escribir datos de forma secuencial y continua.

Cuando se leen los contenidos de archivos byte por byte o se envían y reciben datos a través de una red, todo esto puede tratarse como corrientes de datos.

Al abstraer este mecanismo, archivos, memoria y redes—diferentes fuentes de entrada/salida—pueden manejarse con operaciones comunes como leer y escribir.

El módulo io de Python proporciona una interfaz unificada para corrientes, permitiendo el manejo eficiente de datos tanto de texto como binarios.

Estructura básica del módulo io

El módulo io tiene una jerarquía de tres capas según la naturaleza de las corrientes.

  1. Capa Cruda (RawIOBase)

    RawIOBase maneja la entrada/salida de bytes a nivel más bajo, como descriptores de archivos del sistema operativo y dispositivos.

  2. Capa Búfer (BufferedIOBase)

    BufferedIOBase proporciona una caché (búfer) para mejorar la eficiencia de la E/S. BufferedReader y BufferedWriter son ejemplos típicos.

  3. Capa de Texto (TextIOBase)

    TextIOBase convierte secuencias de bytes en cadenas y gestiona la codificación. Normalmente, al abrir un archivo con la función open(), se utiliza TextIOWrapper de esta capa.

Gracias a esta estructura, el módulo io separa claramente la entrada/salida de texto y binaria, permitiendo combinaciones flexibles.

Estructura básica del módulo io

RawIOBase maneja los descriptores de archivos OS en la capa más baja, BufferedIOBase agrega una caché encima, y la capa superior TextIOBase gestiona conversiones de cadenas.

1import io
2
3# Check the core base classes hierarchy
4print(io.IOBase.__subclasses__())
  • Este código sirve para comprobar el grupo de clases abstractas que heredan de IOBase. Se pueden ver TextIOBase, BufferedIOBase y RawIOBase, confirmando la estructura jerárquica.

io.IOBase: La clase base de todas

IOBase es la clase abstracta base para todos los objetos de E/S, definiendo métodos comunes como close(), flush() y seekable(). Raramente se usa directamente y usualmente se accede a través de clases derivadas.

1import io
2
3f = io.StringIO("data")
4print(f.seekable())   # True
5print(f.readable())   # True
6print(f.writable())   # True
7f.close()
  • Este ejemplo muestra que los métodos comunes de IOBase también pueden usarse en las clases superiores. seekable() y readable() son útiles para comprobar las propiedades de una corriente.

io.RawIOBase: La capa de más bajo nivel

RawIOBase es la capa más cercana al descriptor de archivos del sistema operativo y no realiza almacenamiento en búfer. La implementación típica es FileIO, que lee y escribe por byte.

1import io, os
2
3# Create a low-level FileIO object (no buffering)
4fd = os.open('raw_demo.bin', os.O_RDWR | os.O_CREAT)
5raw = io.FileIO(fd, mode='w+')
6raw.write(b'abc123')
7raw.seek(0)
8print(raw.read(6))  # b'abc123'
9raw.close()
  • FileIO es una implementación concreta de RawIOBase; todas las lecturas y escrituras se realizan como bytes. La eficiencia puede mejorarse combinándolo con la capa superior BufferedIOBase.

io.BufferedIOBase: Capa intermedia (con búfer)

BufferedIOBase es una capa intermedia que realiza almacenamiento en búfer, haciendo el acceso al disco más eficiente. Las implementaciones principales son BufferedReader, BufferedWriter, BufferedRandom y BufferedRWPair.

1import io
2
3# Create a buffered binary stream on top of a BytesIO (simulate file)
4base = io.BytesIO()
5buffered = io.BufferedWriter(base)
6buffered.write(b'Python IO buffering')
7buffered.flush()
8base.seek(0)
9print(base.read())  # b'Python IO buffering'
  • En este ejemplo, los datos escritos con BufferedWriter se almacenan temporalmente en un búfer de memoria y se transfieren a la capa inferior al llamar a flush().

Ejemplo de BufferedReader

BufferedReader es un flujo en búfer de solo lectura que permite lecturas eficientes con peek() y read().

1import io
2
3stream = io.BytesIO(b"1234567890")
4reader = io.BufferedReader(stream)
5print(reader.peek(5))   # b'12345' (non-destructive)
6print(reader.read(4))   # b'1234'
7print(reader.read(3))   # b'567'
  • peek() solo 'espía' los datos y no mueve el puntero. Al combinarlo con read(), puedes controlar el almacenamiento en búfer de manera flexible.

io.TextIOBase: Capa solo de texto

TextIOBase es una capa de abstracción para manejar cadenas de texto, realizando internamente la decodificación y codificación. Una clase de implementación típica es TextIOWrapper.

 1import io
 2
 3# Wrap a binary stream to handle text encoding
 4binary = io.BytesIO()
 5text_stream = io.TextIOWrapper(binary, encoding='utf-8')
 6text_stream.write("\u3053\u3093\u306B\u3061\u306F")
 7text_stream.flush()
 8
 9# Reset stream position
10binary.seek(0)
11
12# Read bytes once
13data = binary.read()
14
15# Show both raw bytes and decoded text
16print("Raw bytes:", data)
17print("Decoded text:", data.decode('utf-8'))
  • En este ejemplo, TextIOWrapper codifica la cadena a UTF-8 y la escribe en la corriente binaria subyacente.

Ejemplo de lectura con TextIOWrapper

La decodificación se realiza automáticamente al leer.

1import io
2
3binary_data = io.BytesIO("Python I/O".encode('utf-8'))
4text_reader = io.TextIOWrapper(binary_data, encoding='utf-8')
5print(text_reader.read())  # 'Python I/O'
  • TextIOWrapper sirve como la clase fundamental para la E/S de texto y forma la base para casi todas las operaciones de archivos de alto nivel.

io.StringIO: Corriente de texto en memoria

StringIO es una clase que permite manejar cadenas de texto en memoria como si fueran archivos. Es útil para pruebas de E/S y generación de datos temporales.

1import io
2
3text_buf = io.StringIO()
4text_buf.write("In-memory text stream")
5text_buf.seek(0)
6print(text_buf.read())  # 'In-memory text stream'
  • StringIO permite operaciones similares a archivos sin usar el disco y es ampliamente utilizado en pruebas unitarias.

io.BytesIO: Corriente binaria en memoria

BytesIO es una clase de archivo en memoria para manejar secuencias de bytes (bytes). Es útil para situaciones como procesamiento binario o compresión de datos donde no se desea utilizar archivos.

1import io
2
3buf = io.BytesIO()
4buf.write(b'\x01\x02\x03')
5buf.seek(0)
6print(list(buf.read()))  # [1, 2, 3]
  • BytesIO tiene la misma interfaz que BufferedIOBase y puede usarse como sustituto de muchas APIs de archivos.

Corrientes personalizadas (creación de clases originales)

Las clases en io son extensibles, permitiendo crear tus propias clases de corrientes. A continuación se muestra un ejemplo de una subclase de TextIOBase que pone en mayúscula todo el texto al escribir.

 1import io
 2
 3class UpperTextIO(io.TextIOBase):
 4    def __init__(self):
 5        self.buffer = ""
 6    def write(self, s):
 7        self.buffer += s.upper()
 8        return len(s)
 9
10u = UpperTextIO()
11u.write("hello io")
12print(u.buffer)  # "HELLO IO"
  • Mientras cumplas el contrato de TextIOBase, puedes definir cualquier comportamiento personalizado como este. También es fácil extender las corrientes para usos específicos, como archivos y redes.

Resumen

El módulo io organiza el procesamiento de entrada/salida en una jerarquía de clases abstractas y concretas.

  • RawIOBase es una clase para la entrada/salida de bytes a nivel de sistema operativo.
  • BufferedIOBase es una clase que ofrece una capa de caché eficiente.
  • TextIOBase es una clase que gestiona la lectura y escritura de cadenas.
  • StringIO y BytesIO son clases que proporcionan corrientes en memoria.

Comprender estas clases te permite captar con precisión el funcionamiento del sistema de E/S de Python y aplicarlas a operaciones de archivos, comunicación en red y diseño de corrientes para pruebas.

Puedes seguir el artículo anterior utilizando Visual Studio Code en nuestro canal de YouTube. Por favor, también revisa nuestro canal de YouTube.

YouTube Video