memoryview count e index: busque sem copiar

Publicado em: 20/10/2026
Tempo de leitura: 5 minutos
Dados binários e memória de computador representando memoryview no Python

O memoryview permite acessar dados binários sem copiar o conteúdo para outro objeto. Em versões recentes do Python, os métodos count() e index() tornam esse tipo ainda mais conveniente para buscas simples dentro de buffers. Eles aproximam a experiência de uso de uma visão de memória daquela já conhecida em listas, tuplas e sequências de bytes, mas preservando a principal vantagem do protocolo de buffer: trabalhar diretamente sobre a memória compartilhada pelo objeto original.

Esse recurso é útil em processamento de arquivos binários, protocolos de rede, imagens, áudio, arrays numéricos e integrações com bibliotecas que expõem buffers. Ao combinar memoryview com operações de busca, você pode localizar marcadores, validar valores, contar ocorrências e implementar parsers sem criar cópias desnecessárias.

O que é memoryview

Um objeto memoryview representa uma janela sobre um buffer existente. O buffer pode vir de bytes, bytearray, array.array, mmap ou de uma extensão que implemente o protocolo de buffer. Em vez de duplicar os dados, a visão mantém uma referência ao armazenamento original.

dados = bytearray([10, 20, 30, 20, 40])
visao = memoryview(dados)
print(visao[1])

Se o objeto subjacente for mutável, alterações feitas pela visão podem aparecer no objeto original. Isso é importante para desempenho, mas também exige cuidado com o ciclo de vida e a validade do buffer.

Como usar count()

O método count(valor) retorna quantas vezes um elemento aparece na visão. Em uma visão unidimensional de bytes, o valor normalmente é um inteiro entre 0 e 255.

dados = bytearray([1, 2, 3, 2, 2, 4])
visao = memoryview(dados)
quantidade = visao.count(2)
print(quantidade)  # 3

A operação percorre os elementos visíveis da sequência. Ela não cria uma lista temporária e não converte o buffer inteiro em bytes, o que ajuda a manter o consumo de memória previsível.

Como usar index()

O método index(valor) retorna a posição da primeira ocorrência. Caso o valor não exista, é levantada uma exceção ValueError.

dados = bytearray([5, 8, 13, 21, 34])
visao = memoryview(dados)
posicao = visao.index(13)
print(posicao)  # 2

Você pode tratar a ausência explicitamente:

try:
    posicao = visao.index(99)
except ValueError:
    posicao = -1

Esse padrão é adequado quando não encontrar o valor é uma situação esperada. Em código de validação, deixar a exceção subir também pode ser útil, pois evidencia imediatamente que o formato recebido não corresponde ao esperado.

Buscas com intervalo

Assim como em outras sequências, a busca pode ser restringida por índices inicial e final. Isso é importante para analisar apenas uma região de um pacote binário.

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

Ao trabalhar com cabeçalhos, metadados e payloads, limitar a região evita que um marcador encontrado em outra parte do buffer seja interpretado incorretamente.

Exemplo prático com protocolo binário

Imagine que cada mensagem termine com o byte 0x0A. Você pode localizar o terminador sem converter todo o buffer:

TERMINADOR = 10
buffer = bytearray(b'PING\nRESTO')
visao = memoryview(buffer)
fim = visao.index(TERMINADOR)
mensagem = bytes(visao[:fim])
print(mensagem)

A única cópia ocorre no momento em que a mensagem final é materializada como bytes. Até esse ponto, a inspeção acontece sobre a memória original.

Diferença para bytes.find()

bytes.find() é excelente para localizar subsequências completas, enquanto memoryview.index() é voltado à busca de elementos dentro da visão. Se você precisa procurar uma assinatura com vários bytes, converter uma fatia pequena ou usar uma estratégia específica pode ser mais adequado. Já para delimitadores de um byte, códigos, flags e valores discretos, index() é direto.

Tipos e formatos

Nem toda visão representa bytes simples. Um memoryview pode expor inteiros, floats e estruturas com formatos diferentes. O valor passado para count() ou index() deve ser compatível com o formato dos elementos da visão.

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

Antes de processar dados externos, verifique propriedades como format, itemsize, ndim e shape. Elas ajudam a evitar interpretações erradas.

Fatiamento sem cópia

Uma das maiores vantagens do memoryview é criar subvisões. Uma fatia geralmente continua apontando para o mesmo armazenamento.

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

Observe que o índice retornado é relativo à subvisão. No exemplo, o valor 8 aparece na posição 3 da fatia, embora esteja na posição 8 do buffer original.

Desempenho e complexidade

Tanto count() quanto index() realizam uma varredura linear. Em termos assintóticos, o custo é O(n). A vantagem não está em tornar a busca sublinear, mas em evitar alocações e cópias extras. Em buffers grandes, isso reduz pressão sobre o coletor de lixo e melhora a previsibilidade.

Se o mesmo conjunto for consultado muitas vezes, talvez seja melhor construir um índice auxiliar. Para uma única passagem ou para dados recebidos em streaming, a busca direta costuma ser suficiente.

Cuidados com mutabilidade

Quando a origem é um bytearray, outro trecho do programa pode alterar os dados entre duas operações. Em aplicações concorrentes, proteja o acesso ou trabalhe com um snapshot quando a consistência for mais importante que evitar cópias.

Também evite redimensionar um objeto mutável enquanto existem visões exportadas. Dependendo do tipo, isso pode gerar BufferError. Libere a visão com release() ou use um bloco with.

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

Validação segura de entrada

Buffers vindos da rede ou de arquivos devem ser tratados como não confiáveis. Verifique comprimento mínimo, formato, limites de índices e quantidade de delimitadores antes de acessar campos. count() pode confirmar que há exatamente um separador, enquanto index() encontra sua posição.

def separar_cabecalho(buffer):
    visao = memoryview(buffer)
    separador = 0
    if visao.count(separador) != 1:
        raise ValueError('estrutura inválida')
    pos = visao.index(separador)
    return bytes(visao[:pos]), visao[pos + 1:]

Esse padrão combina validação e processamento sem copiar o payload inteiro.

Quando usar

Use esses métodos quando o dado já estiver disponível pelo protocolo de buffer e você precisar contar ou localizar elementos individuais. Eles são especialmente úteis em parsers binários, filtros, validação de pacotes, manipulação de imagens e processamento de grandes blocos de dados.

Para aprofundar conceitos relacionados, veja os artigos sobre pickletools no Python, tokenize no Python, sched no Python e atexit no Python.

Consulte também a documentação oficial de memoryview e a PEP 688 sobre o protocolo de buffer.

Conclusão

memoryview.count() e memoryview.index() simplificam buscas em buffers sem abandonar o modelo de acesso sem cópia. Eles não substituem algoritmos especializados, mas oferecem uma solução clara para localizar valores, validar delimitadores e analisar regiões de memória. Com atenção ao formato, à mutabilidade e aos limites do buffer, esses métodos tornam o processamento binário mais expressivo e eficiente.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Desenvolvedora trabalhando com código Python e validação de dados
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    map strict: detecte iteráveis desalinhados

    Aprenda map strict no Python para validar iteráveis, evitar truncamento silencioso e detectar dados desalinhados.

    Ler mais

    Tempo de leitura: 6 minutos
    19/10/2026
    Pastas organizadas representando arquivos e diretórios no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pathlib.Path.info: acelere varreduras de diretórios

    Aprenda pathlib.Path.info no Python para consultar tipos de arquivos com cache, reduzir chamadas ao sistema e evitar dados desatualizados.

    Ler mais

    Tempo de leitura: 6 minutos
    19/10/2026
    Código Python executado em subinterpretadores concorrentes
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    concurrent.interpreters: subinterpretadores no Python

    Aprenda concurrent.interpreters no Python para executar código em subinterpretadores, trocar dados com segurança e explorar paralelismo.

    Ler mais

    Tempo de leitura: 8 minutos
    18/10/2026
    Código Python para leitura precisa de configurações TOML
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tomllib parse_float: decimais precisos no TOML

    Aprenda tomllib parse_float no Python para ler decimais com precisão, validar configurações TOML e usar Decimal com segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    18/10/2026
    Validação de endereços de e-mail com getaddresses strict no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    getaddresses strict: valide listas de e-mail

    Aprenda getaddresses strict no Python para analisar listas de e-mail com validação mais rigorosa, segurança e testes.

    Ler mais

    Tempo de leitura: 5 minutos
    17/10/2026
    Código Python representando functools.Placeholder e argumentos posicionais
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    functools.Placeholder: argumentos flexíveis no partial

    Aprenda functools.Placeholder no Python 3.14 para preencher argumentos posicionais flexíveis com partial e partialmethod.

    Ler mais

    Tempo de leitura: 5 minutos
    17/10/2026