Simulador de Enumerate em Python
Saída do Simulador
Se você já programou em Python por mais de um mês, já deve ter sentido aquela sensação: python tricks são como segredos que só os veteranos conhecem. Não são coisas que você aprende em tutoriais básicos. Não estão no documento oficial. Mas são exatamente esses pequenos truques que transformam código mediano em código elegante, rápido e fácil de manter.
Use enumerate() em vez de range(len())
Quantas vezes você já escreveu algo assim?
for i in range(len(items)):
print(i, items[i])
Isso funciona. Mas é feio. E lento. O Python tem uma função feita exatamente para isso: enumerate(). Ela devolve tanto o índice quanto o valor em uma única iteração.
for i, item in enumerate(items):
print(i, item)
É mais legível, mais rápido e evita erros comuns, como esquecer de atualizar o índice ou usar len() em algo que não é uma lista. E se você quiser começar a contar de 1? Basta passar start=1:
for i, item in enumerate(items, start=1):
print(f"{i}. {item}")
Desempacotamento de variáveis: mais do que só trocar valores
Todos sabem que isso troca duas variáveis:
a, b = b, a
Mas e se você tiver uma lista com 5 elementos e só quiser os dois primeiros?
first, second, *rest = [1, 2, 3, 4, 5]
Agora first é 1, second é 2, e rest é [3, 4, 5]. Isso é útil quando você lida com dados de APIs, arquivos CSV ou respostas de funções que retornam múltiplos valores. E se você só quiser o último?
*start, last = [1, 2, 3, 4, 5]
Agora last é 5, e start é tudo o que veio antes. Nada de items[-1] ou pop() desnecessário.
Compreensão de lista e dicionário: o que você não sabia que podia fazer
Compreensão de lista é clássica:
squares = [x**2 for x in range(10)]
Mas e se você quiser filtrar e transformar ao mesmo tempo?
even_squares = [x**2 for x in range(10) if x % 2 == 0]
E se você precisar de um dicionário? É só trocar os colchetes por chaves:
squares_dict = {x: x**2 for x in range(10)}
Isso é mais rápido que criar uma lista vazia e usar append() ou dict[key] = value em um loop. E funciona com qualquer expressão:
name_lengths = {name: len(name) for name in ["Alice", "Bob", "Charlie"]}
Resultado: {"Alice": 5, "Bob": 3, "Charlie": 7}. Simples, direto, e eficiente.
Use get() em dicionários - e não if key in dict
Quando você acessa uma chave que não existe, Python levanta um KeyError. Muita gente resolve assim:
if "email" in user:
email = user["email"]
else:
email = "não informado"
Isso funciona. Mas é verbose. O jeito certo é usar get():
email = user.get("email", "não informado")
Se a chave existir, retorna o valor. Se não existir, retorna o valor padrão. E se você quiser que o padrão seja None? Não precisa escrever nada:
email = user.get("email")
Isso é mais rápido, mais limpo e menos propenso a erros. E funciona com qualquer tipo de valor - strings, listas, até funções.
Use collections.defaultdict para evitar chaves faltando
Imagine que você está contando quantas vezes cada palavra aparece em um texto. Com um dicionário normal, você teria que fazer isso:
word_count = {}
for word in text.split():
if word in word_count:
word_count[word] += 1
else:
word_count[word] = 1
Com defaultdict, você define o valor padrão quando o dicionário é criado:
from collections import defaultdict
word_count = defaultdict(int)
for word in text.split():
word_count[word] += 1
Isso elimina a verificação. O defaultdict(int) devolve 0 automaticamente para chaves que não existem. Você pode usar list, set, ou até uma função personalizada. É um dos truques mais usados por programadores Python experientes - e por uma boa razão: reduz código em até 60%.
Use itertools para operações de iteração avançadas
Python vem com uma biblioteca chamada itertools que muitos ignoram. Ela tem funções feitas para trabalhar com iteradores de forma eficiente. Por exemplo:
itertools.chain()- junta várias listas em uma única iteraçãoitertools.groupby()- agrupa itens consecutivos por chaveitertools.islice()- pega apenas os primeiros N itens de um iterador
Quer saber os 5 primeiros números pares de uma lista enorme sem carregar tudo na memória?
from itertools import islice
even_numbers = (x for x in range(1000000) if x % 2 == 0)
first_five = list(islice(even_numbers, 5))
Resultado: [0, 2, 4, 6, 8]. E você nem criou uma lista com 500 mil números. Isso economiza memória e tempo. É o tipo de truque que faz seu código escalar sem esforço.
Use contextlib para gerenciar recursos sem try/finally
Quando você abre um arquivo, uma conexão de banco ou uma rede, precisa garantir que ela seja fechada. A maioria das pessoas usa try/finally:
f = open("file.txt")
try:
data = f.read()
finally:
f.close()
Mas o Python tem uma maneira melhor: with. E se você quiser criar seu próprio contexto?
from contextlib import contextmanager
@contextmanager
def database_connection():
conn = connect_to_db()
try:
yield conn
finally:
conn.close()
# Uso:
with database_connection() as conn:
result = conn.query("SELECT * FROM users")
Isso é útil quando você precisa de lógica personalizada para abrir e fechar recursos. E funciona com locks, conexões HTTP, sessões, ou qualquer coisa que precise de limpeza.
Use __slots__ para reduzir uso de memória em classes
Se você tem uma classe com centenas ou milhares de instâncias - como objetos de usuário, produtos ou transações - o uso de memória pode explodir. Por padrão, cada instância de classe em Python guarda seus atributos em um dicionário (__dict__). Isso é flexível, mas pesado.
Com __slots__, você diz ao Python quais atributos a classe vai ter, e ele armazena tudo em uma estrutura mais eficiente, como um array.
class User:
__slots__ = ["name", "email", "age", "city"]
def __init__(self, name, email, age, city):
self.name = name
self.email = email
self.age = age
self.city = city
Isso reduz o uso de memória em até 40% e acelera o acesso aos atributos. Não use isso se você precisa adicionar atributos dinamicamente - mas se você sabe exatamente quais campos sua classe vai ter, é um dos truques mais poderosos para otimização.
Use typing mesmo que você não precise - por causa do IDE
Python é dinâmico. Mas isso não significa que você deva ignorar tipos. O módulo typing não muda o comportamento do código - mas muda a experiência do desenvolvedor.
def process_user(user: dict[str, str]) -> list[str]:
return [user["name"], user["email"]]
Seu IDE (VS Code, PyCharm, etc.) agora sabe que user é um dicionário com chaves e valores de string. Ele pode sugerir autocompletar, detectar erros antes de rodar, e até refatorar automaticamente. Isso é especialmente útil em projetos grandes ou em equipe. E não custa nada.
Use pathlib em vez de os.path
Se você ainda está usando os.path.join(), os.listdir() ou os.path.exists(), está programando em 2010. O Python 3.5 trouxe pathlib - uma maneira orientada a objetos para lidar com caminhos de arquivos.
from pathlib import Path
# Antes
path = os.path.join("data", "users", "config.json")
if os.path.exists(path):
with open(path, "r") as f:
data = json.load(f)
# Agora
path = Path("data") / "users" / "config.json"
if path.exists():
data = json.loads(path.read_text())
É mais legível, mais seguro e mais fácil de encadear. Você pode usar path.iterdir() para listar arquivos, path.parent para acessar a pasta pai, path.stem para o nome sem extensão. E funciona em Windows, macOS e Linux sem mudar nada.
Use functools.lru_cache para memoização automática
Se você tem uma função que é chamada várias vezes com os mesmos argumentos - como calcular fatorial, buscar dados de API ou processar strings - você está desperdiçando tempo. O functools.lru_cache armazena os resultados automaticamente.
from functools import lru_cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
Com isso, a função nunca calcula o mesmo valor duas vezes. O cache é gerenciado automaticamente. Se o cache encher, os valores menos usados são descartados. Isso pode reduzir o tempo de execução de minutos para milissegundos em funções recursivas ou caras.
Evite is None e use not para valores falsos
Python tem valores que são considerados "falsos" em contexto booleano: None, 0, "", [], {}. Muita gente escreve isso:
if my_list is not None and len(my_list) > 0:
Mas isso é redundante. Se my_list for None, not my_list já é True. Então:
if not my_list:
Funciona para listas vazias, strings vazias, dicionários vazios, e None. É mais curto, mais rápido e mais Pythonico. Só não use isso se você precisa distinguir entre 0 e None - aí você precisa ser explícito.
Use __repr__ e __str__ corretamente
Quando você imprime um objeto, Python chama __str__. Se não existir, chama __repr__. Mas a maioria dos programadores esquece de definir __repr__.
__str__ é para o usuário final. __repr__ é para o desenvolvedor. O ideal é que __repr__ devolva uma string que, se executada, criaria o mesmo objeto.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"{self.name}, {self.age} anos"
def __repr__(self):
return f"Person(name='{self.name}', age={self.age})"
p = Person("Ana", 28)
print(p) # Ana, 28 anos
print(repr(p)) # Person(name='Ana', age=28)
Isso faz toda a diferença quando você está depurando. Seu console não mostra <__main__.Person object at 0x7f8b2c1d3d30> - mostra o objeto real.
Conclusão: Python tricks não são mágica - são prática
Esses truques não são segredos de gênios. São hábitos de quem passou tempo escrevendo código real. Eles não fazem seu código "mais inteligente" - fazem ele mais rápido, mais limpo e mais fácil de manter. O Python não é uma linguagem que exige truques. Mas ele te oferece ferramentas para escrever código melhor - se você souber usá-las.
Comece por um. Escolha um desses truques e use ele no próximo projeto. Depois, outro. Em poucas semanas, você vai notar que seu código não parece mais "Python" - ele parece profissional.
O que são Python tricks?
Python tricks são técnicas, atalhos e padrões que programadores experientes usam para escrever código mais eficiente, legível e idiomático em Python. Eles não são parte da documentação oficial, mas são amplamente adotados pela comunidade porque melhoram a qualidade do código sem complicar.
Esses truques funcionam em todas as versões do Python?
A maioria dos truques listados funciona desde o Python 3.5 em diante. Alguns, como pathlib e typing, foram introduzidos em versões mais recentes, mas são suportados em todas as versões atuais (3.8+). Evite truques que exigem Python 3.10+ se você precisa de compatibilidade com sistemas mais antigos.
Posso usar __slots__ em qualquer classe?
Não. __slots__ só deve ser usado em classes que têm muitas instâncias e cujos atributos são fixos. Ele impede a adição dinâmica de atributos, então não use em classes que precisam de flexibilidade, como modelos de banco de dados ou objetos que recebem configurações externas.
Por que enumerate() é melhor que range(len())?
Porque enumerate() é mais legível, mais rápido e menos propenso a erros. Ele evita a necessidade de acessar índices manualmente, o que pode causar erros de deslocamento ou problemas com iteradores que não suportam indexação. Além disso, o Python otimiza enumerate() internamente, tornando-o mais eficiente.
Como saber se um truque é "Pythonico"?
Um truque é Pythonico se ele é claro, simples e tira proveito das características da linguagem - como compreensões, iteradores, e métodos embutidos. O "Zen do Python" diz: "Bonito é melhor que feio" e "Simples é melhor que complexo". Se o código parece que foi escrito por um humano que conhece Python, é provavelmente Pythonico.