memoryview no Python: buffers sem cópia

Publicado em: 28/08/2026
Tempo de leitura: 5 minutos
A close-up shot showcasing the intricate scales of a snake, highlighting texture and color.

Copiar dados grandes pode ser um custo invisível em programas Python. Arquivos binários, pacotes de rede, imagens e matrizes frequentemente passam por várias camadas da aplicação, e cada conversão para bytes ou novo fatiamento pode duplicar memória. O memoryview oferece uma alternativa: ele cria uma visão sobre um objeto que suporta o protocolo de buffer, permitindo ler ou modificar regiões sem copiar todo o conteúdo.

Neste guia, você aprenderá como criar memoryview, usar slices sem cópia, interpretar formatos, converter dimensões, trabalhar com dados mutáveis, integrar com array, bytearray, mmap e struct, além de evitar armadilhas de ciclo de vida e compatibilidade.

O que é o protocolo de buffer?

O protocolo de buffer permite que um objeto exponha sua memória interna para outras partes do Python sem precisar transformá-la em uma nova sequência. Tipos como bytes, bytearray, array.array e objetos retornados por bibliotecas científicas podem participar desse protocolo.

Quando você escreve dados[100:200] em um objeto bytes, o Python cria outro objeto com os bytes selecionados. Com memoryview(dados)[100:200], você recebe apenas uma nova visão que aponta para a mesma memória.

dados = b"abcdefghijklmnopqrstuvwxyz"
visao = memoryview(dados)
parte = visao[5:10]

print(bytes(parte))  # b'fghij'
print(parte.obj is dados)  # True

Esse comportamento é valioso quando o volume de dados é alto ou quando várias etapas precisam examinar o mesmo buffer.

bytes, bytearray e mutabilidade

Uma visão herda a mutabilidade do objeto original. bytes é imutável, portanto a visão é somente leitura. bytearray pode ser modificado por meio da visão.

buffer = bytearray(b"ABCDE")
visao = memoryview(buffer)
visao[1:4] = b"xyz"

print(buffer)  # bytearray(b'AxyzE')

O tamanho da substituição precisa ser compatível com o slice. O memoryview não funciona como uma lista capaz de crescer durante a atribuição; ele representa uma região de memória com formato e tamanho definidos.

Slices sem cópia

Fatiamentos sucessivos continuam compartilhando o mesmo armazenamento. Isso permite criar parsers que entregam campos como visões, evitando alocações intermediárias.

def separar_cabecalho(pacote: bytes):
    view = memoryview(pacote)
    versao = view[0:1]
    tamanho = view[1:5]
    payload = view[5:]
    return versao, tamanho, payload

Enquanto as views existirem, o objeto base deve permanecer válido. Em objetos redimensionáveis, como bytearray, uma view ativa impede operações que alterem o tamanho do buffer.

dados = bytearray(b"1234")
view = memoryview(dados)
# dados.extend(b"5")  # BufferError enquanto view estiver ativa
view.release()
dados.extend(b"5")

Formatos e itemsize

Um memoryview conhece o formato dos elementos, o tamanho de cada item e, em alguns casos, as dimensões. Uma visão sobre array('I'), por exemplo, representa inteiros sem sinal em vez de bytes isolados.

from array import array

numeros = array('I', [10, 20, 30, 40])
view = memoryview(numeros)

print(view.format)
print(view.itemsize)
print(view.tolist())

O atributo nbytes informa o número total de bytes, enquanto len(view) normalmente representa a quantidade de elementos na primeira dimensão.

cast: reinterpretando o buffer

O método cast() permite reinterpretar os mesmos bytes com outro formato compatível. Isso não converte os valores; apenas muda a forma de enxergar a memória.

dados = bytearray(range(16))
bytes_view = memoryview(dados)
matriz = bytes_view.cast('B', shape=[4, 4])

print(matriz[2, 3])
matriz[0, 0] = 255
print(dados[0])

O número total de bytes precisa permanecer consistente. Para protocolos binários complexos, combine memoryview com o módulo struct no Python, que interpreta campos com endianness e layouts explícitos.

Leitura eficiente de arquivos e mmap

O memoryview funciona muito bem com arquivos mapeados em memória. Um mmap pode expor o arquivo como buffer, e views podem representar regiões específicas.

import mmap

with open("dados.bin", "r+b") as arquivo:
    with mmap.mmap(arquivo.fileno(), 0) as mapa:
        view = memoryview(mapa)
        cabecalho = view[:64]
        print(bytes(cabecalho[:8]))
        cabecalho.release()
        view.release()

Libere as views antes de fechar o mapeamento. Caso contrário, o Python pode gerar BufferError. Veja também o guia de mmap no Python.

Integração com sockets

APIs como socket.recv_into() podem gravar diretamente em um buffer reutilizável, reduzindo a criação de objetos.

buffer = bytearray(65536)
view = memoryview(buffer)

# quantidade = sock.recv_into(view)
# pacote = view[:quantidade]

Em servidores de alto volume, buffers reutilizáveis ajudam a controlar pressão sobre o coletor de lixo. No entanto, o código precisa garantir que uma região não seja reutilizada enquanto outra tarefa ainda depende dela.

Comparando bytes e memoryview

Use bytes quando precisar de um valor independente, imutável, fácil de armazenar ou usar como chave de dicionário. Use memoryview quando quiser processar regiões de um buffer sem cópia, modificar dados existentes ou passar memória entre APIs compatíveis.

Converter uma view para bytes cria uma cópia. Faça isso apenas no limite onde um objeto independente seja realmente necessário.

Cuidados com o ciclo de vida

  • Não redimensione um bytearray enquanto houver views exportadas.
  • Libere views antes de fechar mmap ou outros recursos subjacentes.
  • Não guarde uma view por tempo indefinido se o buffer pertence a um pool reutilizável.
  • Documente se uma função retorna dados independentes ou uma visão compartilhada.
  • Use with memoryview(obj) as view quando quiser liberação determinística.

Exemplo: parser binário sem cópias intermediárias

import struct

HEADER = struct.Struct("!BI")

def analisar_pacote(dados: bytes):
    view = memoryview(dados)
    if view.nbytes < HEADER.size:
        raise ValueError("pacote incompleto")

    tipo, tamanho = HEADER.unpack_from(view, 0)
    inicio = HEADER.size
    fim = inicio + tamanho

    if fim > view.nbytes:
        raise ValueError("payload truncado")

    payload = view[inicio:fim]
    return tipo, payload

O payload retornado compartilha a memória com dados. O chamador pode processá-lo sem alocação adicional e convertê-lo somente quando precisar persistir o conteúdo.

Quando o ganho importa?

Para pequenos textos e scripts ocasionais, a simplicidade de bytes costuma ser suficiente. O benefício do memoryview aparece em pipelines binários, processamento de arquivos grandes, redes, multimídia, computação científica e bibliotecas que trocam buffers com código nativo.

Meça antes de otimizar. O custo de cópia pode ser observado com ferramentas de profiling e com o módulo tracemalloc. Uma arquitetura zero-copy mal documentada pode introduzir bugs mais difíceis do que a economia obtida.

Conclusão

O memoryview é a interface nativa do Python para trabalhar com buffers compartilhados. Ele permite criar slices sem cópia, editar regiões mutáveis, reinterpretar formatos e integrar dados binários com módulos como array, struct, mmap e sockets.

A documentação oficial de memoryview no Python 3 detalha formatos, casts e operações suportadas. Use a ferramenta quando o custo de cópia for relevante, mantendo explícitos a mutabilidade, o proprietário do buffer e o ciclo de vida das views.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    A person typing on a laptop with a Python programming book visible, capturing technology and learning.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tracemalloc no Python: rastreie memória

    Aprenda tracemalloc no Python para medir picos, criar e comparar snapshots, filtrar alocações e diagnosticar crescimento de memória.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    queue no Python: comunique threads

    Aprenda queue no Python para comunicar threads com FIFO, LIFO, prioridade, backpressure, task_done, join, sentinelas e shutdown seguro.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026
    Flat lay of a complete toolset neatly organized in a workshop setting, essential for auto repair tasks.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    selectors no Python: vários sockets

    Aprenda selectors no Python para multiplexar sockets, controlar leitura e escrita parcial, buffers, timeouts, wakeup e backpressure.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    contextvars no Python: contexto assíncrono

    Aprenda contextvars no Python para contexto por task, request IDs, logging, copy_context, propagação a threads e restauração segura com tokens.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026
    Person writing appointments on a calendar with a blue pen. High angle view.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    sched no Python: agende eventos

    Aprenda sched no Python para agendar eventos, usar prioridades, cancelar tarefas, criar recorrência sem drift e integrar executores.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026
    Close-up of a vibrant yellow python coiled with textured scales in vibrant light.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    bisect no Python: listas ordenadas

    Aprenda bisect no Python para busca binária, inserção ordenada, duplicatas, funções key, faixas, rankings e sincronização segura.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026