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

    Código em monitor representando acesso a memória nativa com ctypes.memoryview_at no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ctypes.memoryview_at: acesse memória sem cópias

    Aprenda ctypes.memoryview_at no Python para acessar memória nativa sem cópias, validar ponteiros e evitar erros perigosos.

    Ler mais

    Tempo de leitura: 7 minutos
    12/10/2026
    Código Python em tela representando EnumDict e enumerações avançadas
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    EnumDict: personalize Enums com validação

    Aprenda EnumDict no Python para personalizar EnumType, validar membros e criar enumerações avançadas com segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    11/10/2026
    Rack de servidores representando balanceamento de conexões com SO_REUSEPORT_LB no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    SO_REUSEPORT_LB: distribua conexões entre workers

    Aprenda SO_REUSEPORT_LB no Python para distribuir conexões entre múltiplos workers com segurança, testes e portabilidade.

    Ler mais

    Tempo de leitura: 6 minutos
    11/10/2026
    Código Python assíncrono em notebook para inspect.markcoroutinefunction
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    markcoroutinefunction: identifique wrappers async

    Aprenda inspect.markcoroutinefunction no Python para identificar wrappers assíncronos, integrar frameworks e evitar detecção incorreta de corrotinas.

    Ler mais

    Tempo de leitura: 6 minutos
    10/10/2026
    Código Python para percorrer pastas e arquivos com Path.walk
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    Path.walk: percorra diretórios com segurança

    Aprenda Path.walk no Python para percorrer diretórios, filtrar arquivos, tratar erros e controlar a travessia com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    10/10/2026
    Depuração de processo Python em terminal com código
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pdb -p: depure processos Python em execução

    Aprenda a anexar o pdb a um processo Python em execução, inspecionar pilhas e diagnosticar travamentos com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    09/10/2026