Erros comuns que deixam Python lento: como evitá-los e acelerar seus scripts

Erros comuns que deixam Python lento podem transformar um protótipo simples em um gargalo de desempenho difícil de diagnosticar. Mesmo desenvolvedores experientes escorregam em armadilhas aparentemente inofensivas que, somadas, consomem memória, tempo de CPU e paciência.

Neste guia aprofundado, analisamos sete deslizes frequentes, explicamos por que eles acontecem, apresentamos métricas de impacto e mostramos soluções práticas, sempre com foco em boas práticas de engenharia de software. Ao compreender cada armadilha, você estará mais perto de entregar aplicações rápidas, escaláveis e econômicas.

1. Uso de listas para buscas de pertencimento

O que acontece: ao executar if item in minha_lista:, o interpretador percorre todos os elementos, um a um, até encontrar o valor — ou constatar sua ausência. Esse processo é denominado busca linear, classificado como operação O(n).

Consequência: quanto maior a lista, maior o tempo de espera. Em listas com milhões de itens, cada verificação consome milissegundos preciosos; dentro de loops aninhados, o atraso se multiplica.

Por que é lento: listas não são indexadas por hash. O interpretador precisa comparar objeto por objeto, realizando verificação de igualdade e tratamento de exceções para cada entrada.

Correção que importa: troque a lista por um set ou por um dict usado apenas como conjunto de chaves. Ambos utilizam tabela de espalhamento (hash map) e oferecem busca O(1) na média.

Exemplo prático:


# Lento
usuarios_repetidos = [u for u in lista_a if u in lista_b]

# Rápido
conjunto_b = set(lista_b)
usuarios_repetidos = [u for u in lista_a if u in conjunto_b]

Métrica de ganho: em teste com 1 milhão de itens, a versão com set executou em 0,3 s, contra 8,4 s da busca em lista — ganho de 28×.

2. O temido problema de consultas N+1

Definição: sua aplicação realiza uma consulta inicial (1) e, para cada linha retornada (N), aciona uma nova busca ao banco ou a uma API. O ciclo se repete e sobrecarrega o back-end.

Impacto real: em um cenário com 5 000 registros, cada requisição extra de 10 ms gera 50 s de espera. Em ambiente web, o efeito cascata pode bloquear threads, derrubar latência e elevar custos de infraestrutura.

Por que surge: ORMs tornam o código elegante, mas escondem detalhes de join entre tabelas. Muitas vezes, um simples acesso a atributo relacionado dispara nova query sem que o programador perceba.

Estratégias de mitigação:

a) Eager loading: carregue relações necessárias com .select_related() ou .prefetch_related() (Django), joinedload() (SQLAlchemy) ou equivalentes.

b) Aggregation: consolide dados no servidor de bancos, usando JOIN, GROUP BY ou pipelines específicos (ex.: $lookup no MongoDB).

c) Cache inteligente: memorize resultados em memória (Redis, Memcached) quando justificar.

Bônus: para APIs externas, implemente batch requests ou endpoints que retornem dados agregados.

3. Concatenar strings dentro de loops

O problema: strings são imutáveis. Ao executar texto += "novo", o interpretador precisa:

1. Reservar nova área de memória para o conteúdo acumulado.
2. Copiar o texto antigo.
3. Acrescentar o sufixo.
4. Liberar a memória anterior para garbage collection.

Resultado: milhares de realocações médium a grandes elevam o tempo de CPU e a pressão sobre o coletor de lixo.

Abordagem ideal: acumule pequenos trechos em uma lista e, ao final, use "".join(lista). O método join calcula o tamanho total uma única vez e faz cópia linear otimizada em C.

Caso de uso:


# Lento
saida = ""
for linha in linhas:
  saida += linha

# Rápido
buffer = [] for linha in linhas:
  buffer.append(linha)
saida = "".join(buffer)

Evolução de consumo: experimento com 100 000 linhas reduziu a execução de 1,9 s para 0,12 s e o pico de memória caiu de 220 MB para 28 MB.

4. Ler arquivos inteiros para a memória

Cenário típico: utilizar f.read() ou f.readlines() para carregar de uma só vez logs, CSVs ou JSONs.

Complicação: acima de algumas centenas de megabytes, o script dispara MemoryError. Mesmo que não quebre, o sistema operacional inicia troca de páginas (swap), penalizando todo o servidor.

Melhor prática: tratar o manipulador de arquivo como iterador nativo.


with open("grande.csv") as f:
  for linha in f:
    processa(linha)

Benefício: cada linha é processada e descartada, mantendo o uso de RAM constante, independentemente do tamanho do arquivo.

Alternativas:

Erros comuns que deixam Python lento: como evitá-los e acelerar seus scripts - Imagem do artigo original

Imagem:  Lucas Gouveia

Generator expressions para pipelines.
Módulos especializados como csv, gzip e jsonlines.
Bibliotecas que suportam streaming (Pandas 2.0 read_csv_iterator, PyArrow).

5. Loops aninhados ineficientes

Diagnóstico: loops duplos que percorrem milhões de combinações criam complexidade O(). Esse padrão costuma aparecer em

• comparação de duas listas;
• detecção de duplicados;
• joins manuais.

Ilustração simplificada:


for a in lista_a:
  for b in lista_b:
    if a == b:
      …

Refatoração: converta a coleção interna em estrutura de acesso O(1), como set ou dict. Você passa de O() para O(n).

Pensamento de dados primeiro: muitas vezes vale pré-processar listas para remover duplicações, ordenar ou indexar antes de entrar no laço principal. Investir tempo nesse preparo compensa pelas ordens de grandeza economizadas.

6. Abrir e fechar recursos repetidamente

Situação: dentro de um for, o desenvolvedor chama open(), cria conexão com banco ou estabelece sessão HTTP a cada iteração.

Custo oculto: cada abertura envolve chamadas de sistema, validação de permissões, alocação de descritores e, nos casos de rede, handshakes de TLS. Tudo isso representa latência e carga de CPU.

Remédio: abra o recurso uma única vez, antes do loop, por meio de context manager — garantindo fechamento seguro mesmo em caso de exceções.


with open("saida.log", "w") as log:
  for dado in dados:
    log.write(formatar(dado))

Para bancos de dados, reuse connection pools. Para chamadas web, mantenha objeto Session (requests) ou aiohttp.ClientSession aberto.

7. Ignorar funções otimizadas da biblioteca padrão

Motivação equivocada: tentar “ter controle total” implementando de próprio punho algoritmos de ordenação, soma ou filtragem.

Dor de cabeça: loops Python puros implicam overhead interpretado em cada iteração: verificação de tipo, checagem de limites, gerenciamento de pilha. Já funções escritas em C dentro da CPython são vetorizadas, usam ponteiros e eliminam verificações repetitivas.

Ferramentas nativas poderosas:

sum(), min(), max();
sorted(), bisect;
itertools: chain, islice, groupby;
functools: lru_cache, reduce.

Quando combinadas, essas funções bastam para a maioria das manipulações sem sacrificar clareza.

Exemplo comparativo — soma de quadrados pares até 1 000 000:


# Versão manual em loop
total = 0
for i in range(1_000_000):
  if i % 2 == 0:
    total += i * i

# Versão otimizada
total = sum(i * i for i in range(0, 1_000_000, 2))

Além de mais legível, a segunda alternativa foi 4,8 × mais rápida em testes de bancada.

Boas práticas gerais para evitar lentidão

1. Perfil antes de otimizar: utilize cProfile, timeit, py-instrument ou snakeviz para encontrar pontos críticos em vez de adivinhar.

2. Pense em algoritmos: escolha estruturas de dados adequadas (listas, dicionários, heaps) de acordo com o padrão de acesso.

3. Prefira clareza: código limpo costuma ser mais fácil de otimizar. Complexidade prematura pode esconder gargalos.

4. Considere paralelismo: para I/O use asyncio ou threading. Para CPU intensivo, avalie multiprocessing ou Cython.

5. Revise dependências: bibliotecas desatualizadas podem carregar algoritmos ineficientes. Mantenha seu ambiente moderno.

Conclusão

Python oferece produtividade excepcional, mas essa agilidade pode mascarar armadilhas que comprometem o desempenho. Ao reconhecer e corrigir os sete erros descritos — da escolha equivocada de listas para buscas até o desprezo por funções otimizadas — você garante aplicações mais rápidas, escaláveis e baratas. Otimizar não significa escrever código obscuro; significa entender como a linguagem opera e tomar decisões conscientes. Revise seus projetos hoje mesmo, aplique as recomendações e perceba a diferença na experiência do usuário e no uso de recursos.


Com informações de How-To Geek

Total
0
Shares
Related Posts
NowConecta
Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.