memoryview count e index: busca sin copiar

Publicado el: 20/10/2026
Tempo de leitura: 5 minutos
Datos binarios y memoria de ordenador representando memoryview en Python

memoryview permite acceder a datos binarios sin copiar todo el contenido a otro objeto. Los métodos count() e index() facilitan búsquedas comunes dentro de buffers y mantienen la ventaja del protocolo de buffer: la vista apunta al almacenamiento original.

Este enfoque es útil al procesar archivos binarios, paquetes de red, imágenes, audio, arrays numéricos y extensiones. Con una vista de memoria es posible localizar marcadores, validar campos, contar valores y analizar estructuras evitando asignaciones temporales innecesarias.

Qué representa memoryview

Un objeto memoryview es una ventana sobre un buffer existente. El almacenamiento puede provenir de bytes, bytearray, array.array, mmap o un tipo externo que implemente el protocolo de buffer. Crear la vista normalmente no duplica los datos.

datos = bytearray([10, 20, 30, 20, 40])
vista = memoryview(datos)
print(vista[1])

Si el origen es modificable, los cambios realizados mediante la vista pueden afectar al objeto original. Este comportamiento sin copias mejora el rendimiento, pero exige cuidar la mutabilidad y la vida útil del buffer.

Cómo usar count()

El método count(valor) devuelve cuántas veces aparece un elemento en la secuencia visible. En una vista unidimensional de bytes, el valor buscado suele ser un entero entre 0 y 255.

datos = bytearray([1, 2, 3, 2, 2, 4])
vista = memoryview(datos)
cantidad = vista.count(2)
print(cantidad)  # 3

La búsqueda recorre directamente la vista. No es necesario convertir todo el buffer en una lista ni en un objeto bytes, por lo que el uso máximo de memoria se mantiene bajo incluso con entradas grandes.

Cómo usar index()

El método index(valor) devuelve la posición de la primera coincidencia. Si el valor no existe, Python lanza ValueError.

datos = bytearray([5, 8, 13, 21, 34])
vista = memoryview(datos)
posicion = vista.index(13)
print(posicion)

Cuando la ausencia es esperada, puede tratarse de forma explícita con try y except. En validaciones estrictas, permitir que la excepción se propague puede ser mejor porque deja claro que la estructura no coincide con el formato esperado.

Limitar el rango de búsqueda

Las búsquedas pueden restringirse con índices de inicio y fin. Esto es importante en protocolos binarios, donde un mismo marcador puede tener significados diferentes en la cabecera, los metadatos o el contenido principal.

datos = bytearray([7, 9, 7, 9, 7, 9])
vista = memoryview(datos)
print(vista.count(9, 2, 6))
print(vista.index(7, 1, 5))

Buscar solo dentro de los límites definidos por el formato evita interpretar accidentalmente un valor situado en otra región del paquete.

Ejemplo con un mensaje binario

Supongamos que cada mensaje termina con el byte 0x0A. El terminador puede localizarse sin convertir todo el buffer:

TERMINADOR = 10
buffer = bytearray(b'PING\nDATOS')
vista = memoryview(buffer)
fin = vista.index(TERMINADOR)
mensaje = bytes(vista[:fin])

La única copia aparece cuando el mensaje final se materializa como un valor independiente. La inspección anterior se realiza sobre el almacenamiento original.

Diferencia frente a bytes.find()

bytes.find() está pensado para localizar subsecuencias completas, mientras que memoryview.index() busca un elemento de la vista. Para una firma de varios bytes puede ser más claro usar find() o un parser especializado. Para delimitadores de un byte, códigos y valores numéricos, index() es directo.

Formatos distintos de bytes

Una vista puede exponer enteros, números de coma flotante y otros formatos. El valor pasado a count() o index() debe ser compatible con el formato de los elementos.

from array import array
numeros = array('i', [10, 20, 10, 30])
vista = memoryview(numeros)
print(vista.count(10))

Al trabajar con datos externos conviene revisar format, itemsize, ndim y shape. Una suposición incorrecta sobre el ancho o el signo de los elementos puede producir resultados equivocados.

Slices sin copia

Al cortar una vista normalmente se crea otra vista que apunta al mismo almacenamiento.

datos = bytearray(range(20))
vista = memoryview(datos)
parte = vista[5:15]
print(parte.index(8))

El índice devuelto es relativo a la subvista. En este ejemplo, el valor 8 ocupa la posición 3 de la parte aunque esté en la posición 8 del buffer original.

Rendimiento y complejidad

Ambos métodos realizan un recorrido lineal, por lo que su complejidad temporal es O(n). La ventaja no consiste en hacer la búsqueda sublineal, sino en evitar la asignación de una copia convertida. En pipelines de datos esto reduce la presión sobre el recolector de basura y mejora la previsibilidad del consumo de memoria.

Si el mismo conjunto inmutable se consulta muchas veces, un índice auxiliar puede ser más rápido. Para un parser de una sola pasada, una trama de red o una entrada en streaming, el recorrido directo suele ser suficiente.

Mutabilidad y concurrencia

Cuando el origen es un bytearray u otro exportador modificable, otra parte del programa puede cambiar los datos entre dos operaciones. Las aplicaciones concurrentes deben sincronizar el acceso cuando necesitan una instantánea coherente.

Redimensionar el objeto exportador mientras existen vistas activas puede lanzar BufferError. Libera la vista cuando termines o usa un administrador de contexto.

datos = bytearray(b'abcabc')
with memoryview(datos) as vista:
    print(vista.count(ord('a')))

Validación segura

Los buffers procedentes de archivos, conexiones o fuentes externas deben considerarse no confiables. Verifica la longitud mínima, los rangos válidos, el formato, las dimensiones y la cantidad de separadores antes de acceder a los campos.

def separar_cabecera(buffer):
    vista = memoryview(buffer)
    separador = 0
    if vista.count(separador) != 1:
        raise ValueError('estructura inválida')
    posicion = vista.index(separador)
    return bytes(vista[:posicion]), vista[posicion + 1:]

Este patrón valida la estructura y mantiene el contenido restante como vista. Otra etapa puede decidir después si debe copiarlo, decodificarlo o conservarlo.

Cuándo usar estos métodos

Usa count() e index() cuando los datos ya están disponibles mediante el protocolo de buffer y necesitas contar o localizar elementos individuales. Son útiles en parsers de paquetes, canales de imágenes, validación de delimitadores, formatos numéricos compactos y escáneres de archivos.

Para ampliar el tema, consulta pickletools en Python, sched en Python, atexit en Python y faulthandler en Python.

La documentación oficial de memoryview describe la API completa y la PEP 688 explica el soporte del protocolo de buffer.

Conclusión

memoryview.count() y memoryview.index() simplifican las búsquedas en buffers sin renunciar al acceso sin copias. No sustituyen algoritmos especializados, pero son excelentes para delimitadores, valores discretos, validación y parsers binarios compactos. Al comprobar el formato, controlar las mutaciones, validar los límites y liberar correctamente las vistas, una aplicación puede utilizarlos de forma segura y eficiente.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Desarrolladora trabajando con código Python y validación de datos
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    map strict: detecta iterables desalineados

    Aprende map strict en Python para validar iterables, evitar truncamientos silenciosos y detectar datos desalineados.

    Ler mais

    Tempo de leitura: 4 minutos
    19/10/2026
    Carpetas organizadas que representan archivos y directorios en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pathlib.Path.info: acelera recorridos de directorios

    Aprende pathlib.Path.info en Python para reutilizar metadatos, acelerar recorridos y evitar decisiones con caché desactualizada.

    Ler mais

    Tempo de leitura: 7 minutos
    19/10/2026
    Código Python ejecutado con subintérpretes concurrentes
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    concurrent.interpreters: subintérpretes en Python

    Aprende concurrent.interpreters en Python para ejecutar tareas aisladas, intercambiar datos con seguridad y gestionar paralelismo.

    Ler mais

    Tempo de leitura: 8 minutos
    18/10/2026
    Código Python para lectura precisa de configuraciones TOML
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tomllib parse_float: decimales precisos en TOML

    Aprende tomllib parse_float en Python para leer decimales TOML con precisión, validación, Decimal y pruebas seguras.

    Ler mais

    Tempo de leitura: 5 minutos
    18/10/2026
    Validación de direcciones de correo con getaddresses strict en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    getaddresses strict: valida listas de correo

    Aprende getaddresses strict en Python para analizar listas de correo con validación rigurosa, seguridad, compatibilidad y pruebas.

    Ler mais

    Tempo de leitura: 5 minutos
    17/10/2026
    Código Python que representa functools.Placeholder y argumentos posicionales
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    functools.Placeholder: argumentos flexibles en partial

    Aprende functools.Placeholder en Python 3.14 para crear llamadas parciales flexibles con argumentos posicionales abiertos.

    Ler mais

    Tempo de leitura: 6 minutos
    17/10/2026