Erros comuns ao programar em Python que você deve evitar

Ao escrever código, desenvolvedores iniciantes em Python raramente consideram a eficiência e a concisão, resultando em código prolixo e com baixo desempenho. Este artigo apresenta alguns exemplos práticos para inspirar iniciantes a escrever código Python de forma mais elegante.

Erro 1: Não utilizar estruturas de dados avançadas

1. Conjuntos (sets)

Muitos iniciantes subestimam o poder dos conjuntos (sets) e tuplas (tuples). Por exemplo, para obter a interseção de duas listas:

def elementos_comuns(lista1, lista2):
    comuns = []
    for item1 in lista1:
        if item1 in lista2:
            comuns.append(item1)
    return comuns

Uma abordagem mais eficiente seria:

def elementos_comuns(lista1, lista2):
    comuns = set(lista1).intersection(set(lista2))
    return list(comuns)

2. Dicionários (dict)

Iniciantes costumam percorrer chaves e valores de um dicionário da seguinte forma:

meu_dict = {'a':1, 'b':2}
for chave in meu_dict:
    print(chave, meu_dict[chave])

Uma maneira mais elegante de realizar esta operação:

meu_dict = {'a':1, 'b':2}
for chave, valor in meu_dict.items():
    print(chave, valor)

Para a maioria dos projetos, esta abordagem é mais eficiente.

3. Tuplas (tuple)

Tuplas são imutáveis após sua criação, mas possuem diversas aplicações úteis. Muitas funções retornam tuplas, como enumerate() e dict.items(), e podem ser usadas em funções para retornar múltiplos valores. Além disso, permitem a extração conveniente de informações:

a, b = ('gato', 'cachorro')

No exemplo acima, os dois elementos da tupla são atribuídos a a e b. O mesmo princípio se aplica a mais valores:

a, b, c = ('gato', 'cachorro', 'tigre')
print(a, b, c)

Para extrair o primeiro e último elementos:

primeiro, *_, final = (1, 2, 3, 4, 5, 6)
print(primeiro, final)
# Saída: 1, 6

Para extrair primeiro, meio e último:

primeiro, *meio, final = (1, 2, 3, 4, 5, 6)
print(primeiro, meio, final)
# Saída: 1, [2, 3, 4, 5], 6

Tuplas também permitem a troca de variáveis:

(a, b, c) = (c, a, b)

Além disso, tuplas podem ser usadas como chaves de dicionário, o que é útil para armazenar dados como coordenadas geográficas (latitude e longitude).

Erro 2: Não utilizar gerenciadores de contexto

É comum ver iniicantes realizando operações de leitura de arquivos desta forma:

if os.path.exists(caminho_arquivo_dados):
    arquivo_dados = open(caminho_arquivo_dados, 'r')
else:
    raise OSERROR
print(arquivo_dados.read())
arquivo_dados.close()

Esta abordagem apresenta problemas significativos:

  • O arquivo pode existir, mas estar bloqueado para leitura
  • A leitura pode ser bem-sucedida, mas ocorrer erros durante o processamento do objeto do arquivo
  • É possível esquecer de fechar o arquivo

A instrução with resolve esses problemas de forma elegante:

with open(caminho_arquivo_dados, 'r') as arquivo_dados:
    print(arquivo_dados.read())

Esta abordagem captura exceções durante a abertura ou processamento do arquivo e garante seu fechamento automático após a conclusão das operações.

Erro 3: Não utilizar a biblioteca padrão

Módulos como itertools e collections são subutilizados por iniciantes.

itertools

Considere a seguinte tarefa com loops aninhados:

lista1 = range(1, 10)
lista2 = range(10, 20)
for item1 in lista1:
    for item2 in lista2:
        print(item1 * item2)

Esta operação pode ser simplificada usando product():

from itertools import product
lista1 = range(1, 10)
lista2 = range(10, 20)
for item1, item2 in product(lista1, lista2):
    print(item1 * item2)

Ambos os códigos produzem o mesmo resultado, mas o segundo é mais conciso e eficiente. A biblioteca itertools oferece diversas funções úteis para iteração, incluindo:

  • count() - cria um iterador infinito
  • cycle() - repete indefinidamente uma sequência
  • chain() - encadeia múltiplos iteráveis
  • group() - agrupa elementos adjacentes iguais

collections

Um cenário comum é a construção de dicionários a partir de dados agrupados:

lista_consolidada = [('a', 1), ('b', 2), ('c', 3), ('b', 4)]
itens_por_id = {}
for id_, item in lista_consolidada:
    if id_ not in itens_por_id:
        itens_por_id[id_] = []
    itens_por_id[id_].append(item)

Este padrão pode ser simplificado usando defaultdict():

from collections import defaultdict

itens_por_id = defaultdict(list)
lista_consolidada = [('a', 1), ('b', 2), ('c', 3), ('b', 4)]

for id_, item in lista_consolidada:
    itens_por_id[id_].append(item)

Neste exemplo, defaultdict(list) retorna uma lista vazia automaticamente quando uma chave não existe.

Outro caso comum é a contagem de frequências:

cores = ['vermelho', 'azul', 'vermelho', 'verde', 'azul', 'azul']
resultado = {}
for cor in cores:
    if resultado.get(cor) is None:
        resultado[cor] = 1
    else:
        resultado[cor] += 1
print(resultado)

Isso pode ser simplificado com defaultdict():

cores = ['vermelho', 'azul', 'vermelho', 'verde', 'azul', 'azul']
contagem = defaultdict(int)
for cor in cores:
    contagem[cor] += 1
print(contagem)

Ou de forma ainda mais concisa com Counter():

from collections import Counter
cores = ['vermelho', 'azul', 'vermelho', 'verde', 'azul', 'azul']
contagem = Counter(cores)
print(dict(contagem))

Para operações de cópia de arquivos, ao invés de usar o módulo os.system:

from os import system
system("xcopy e:\\amostra.csv  e:\\novodiretorio\\")

Utilize o módulo shutil, que fornece relatórios detalhados de erros e exceções:

import shutil
shutil.copyfile('E:\\dados.csv', 'e:\\backup\\dados.csv')

Erro 4: Não utilizar tratamento de exceções

Tanto desenvolvedores experientes quento iniciantes devem implementar tratamento de exceções em seus códigos para garantir maior robustez. Isso é tipicamente feito com instruções try...except.

Erro 5: Não utilizar geradores

A menos que você precise de uma lista complexa e frequentemente acessada, prefira geradores. Eles economizam significativamente a memória. Compare as duas abordagens:

def potencias_de_dois(limite=20000):
    i = 0
    potencias = []
    while 2**i < limite:
        potencias.append(2**i)
        i += 1
    return potencias

Para dados que são utilizados poucas vezes, ocupam muita memória e são fáceis de gerar, use um gerador:

from itertools import count, takewhile

def potencias_de_dois(limite=20000):
    for indice in takewhile(lambda i: 2**i < limite, count(start=0)):
        yield 2**indice

Tags: Python Estruturas de Dados gerenciadores de contexto Coleções itertools

Publicado em 7-11 16:10