Python Essencial
Um checklist dos fundamentos de Python moderno (3.10+): cada tópico em poucas linhas, com o snippet mínimo e as armadilhas. Se ao ler algo você sentir que não domina, o ponteiro → aprofunde em… indica a apostila completa. Serve de revisão pré-entrevista, de mapa para quem está começando, e de referência rápida no dia a dia.
O formato "Essencial"
Uma apostila-checklist: densa, sem rodeios, feita para ser varrida — não lida linha a linha.
- Leia rápido. Se você reconhece o tópico e sabe o snippet de cor, siga. Se travou, é sinal de estudar.
- Os → aprofunde em… apontam para as apostilas completas: Python (do básico ao avançado), Análise de Dados com Python, Algoritmos com Python, FastAPI, Aceleração de Código com IA.
- As caixas ⚠️ armadilha são o que mais cai em entrevista e o que mais gera bug — não pule.
- A auto-checagem no fim de cada seção: se você não responde de cabeça, volte.
- O checklist final é a lista literal para marcar antes de dizer "sei o básico de Python".
Tipos, valores e igualdade
Dinamicamente tipado (o valor tem tipo, não a variável), fortemente tipado (sem coerção maluca). → aprofunde em Python
| Tipo | Mutável? | Notas |
|---|---|---|
int | não | precisão arbitrária (sem overflow) |
float | não | 0.1 + 0.2 != 0.3; math.isclose para comparar |
str | não | Unicode; f-string f"{x}" |
bool | não | subclasse de int: True == 1 |
None | — | o "nada" (equivale a null); teste com is None |
list, dict, set | sim | seção 03 |
tuple, frozenset, bytes | não | hasháveis (podem ser chave de dict) |
type(x) # o tipo isinstance(x, (int, float)) # checagem (aceita tupla de tipos) x == y # igualdade de VALOR x is y # mesma IDENTIDADE (mesmo objeto na memória) x is None # SEMPRE assim para None (nunca == None)
Truthiness
Falsy: False, None, 0, 0.0, "", [], {}, (), set(), e objetos com __len__() == 0. Todo o resto é truthy.
isvs==:issó paraNone(e sentinelas).a is bpode darTruepara inteiros pequenos e strings curtas (cache do CPython) — não confie nisso.if x:quandoxpode ser0ou""válido → useif x is not None:."3" + 3→TypeError(não coage).int("3") + 3.- Strings e tuplas são imutáveis: "modificar" cria um objeto novo.
✔️ Auto-checagem
Diferença entre is e ==, e quando usar cada. Por que x == None é errado. Quais tipos são imutáveis. O que é falsy em Python. Por que True + True == 2.
Nomes, escopo e "tudo é objeto"
Variável = um nome ligado a um objeto. Atribuição nunca copia — só religa o nome. → aprofunde em Python
a = [1, 2, 3] b = a # b e a apontam para a MESMA lista b.append(4) # a agora é [1, 2, 3, 4] ← aliasing c = a[:] # cópia RASA (ou list(a), a.copy()) import copy; d = copy.deepcopy(a) # cópia PROFUNDA
Escopo: LEGB
Ao ler um nome, Python procura em: Local → Enclosing (função externa) → Global (módulo) → Built-in.
x = 10 def f(): global x # para ATRIBUIR a x global de dentro da função x = 20 def outer(): y = 1 def inner(): nonlocal y # para atribuir ao y do enclosing y += 1
- Argumento default mutável:
def f(x=[]):— a lista é criada uma vez e reusada entre chamadas. Usedef f(x=None): x = x or []. - Passar uma lista/dict para uma função e mutá-la lá dentro afeta o chamador (passagem por referência do objeto).
- Atribuir a um nome dentro da função o torna local — ler antes de atribuir dá
UnboundLocalError(a menos deglobal/nonlocal). - Loop variable "vaza" para fora do
for(não tem escopo de bloco).
✔️ Auto-checagem
O que é LEGB? Por que def f(x=[]) é uma armadilha e como corrigir? Diferença entre b = a e b = a[:] numa lista. Quando usar global / nonlocal?
Estruturas de dados
list / tuple / dict / set + comprehensions + slicing. → aprofunde em Python · Algoritmos com Python (complexidade)
| Sintaxe | Uso | Chave/valor | |
|---|---|---|---|
list | [1, 2, 3] | sequência ordenada, mutável | índice; append, pop, sort |
tuple | (1, 2) / 1, 2 | sequência imutável; retorno múltiplo | índice; hasheável |
dict | {"a": 1} | mapa hash, ordem de inserção (3.7+) | d["a"], d.get("a", pad), .items()/.keys()/.values() |
set | {1, 2, 3} | únicos, operações de conjunto (& | - ^) | in é O(1) |
# comprehensions [x*x for x in nums if x % 2 == 0] # list {k: v for k, v in pares} # dict {x % 3 for x in nums} # set (x for x in nums) # generator (lazy, não é tuple comprehension) # slicing (funciona em list, str, tuple) a[1:4] a[::2] a[::-1] a[:-1] # start:stop:step # unpacking primeiro, *meio, ultimo = [1, 2, 3, 4, 5] # meio = [2, 3, 4] d = {**d1, **d2} # merge de dicts (d2 vence) f(*args, **kwargs) # espalhar em chamada # idiomas úteis from collections import Counter, defaultdict, deque Counter(lista).most_common(3) d = defaultdict(list); d[k].append(v) # sem checar se a chave existe
list.sort()muta e retornaNone;sorted(list)retorna nova lista. Idemreverse()vsreversed().d[chave]lançaKeyErrorse ausente;d.get(chave)retornaNone. Usegetoudefaultdict.[[0]*3]*3cria 3 referências para a mesma linha. Use[[0]*3 for _ in range(3)].x in listaé O(n);x in set/x in dicté O(1). Para muitas checagens, use set.- Modificar uma lista enquanto itera sobre ela → comportamento errático. Itere sobre uma cópia (
for x in lista[:]) ou construa uma nova.
✔️ Auto-checagem
Diferença entre sort e sorted. Como inverter uma lista/string com slicing. Por que [[0]*3]*3 é uma cilada. Quando set em vez de list. Escreva um dict comprehension que inverte chave↔valor.
Controle de fluxo
Indentação é a sintaxe (4 espaços). → aprofunde em Python
if cond: ... elif outra: ... else: ... for i, item in enumerate(lista, start=1): # índice + valor ... for a, b in zip(xs, ys): # iterar em paralelo (para no menor) ... for x in lista: if achou: break else: # roda se o for NÃO deu break print("não achou") while cond: ... if pular: continue # match (3.10+) — pattern matching, não é só switch match ponto: case (0, 0): print("origem") case (x, 0) | (0, x): print("eixo") case (x, y) if x == y: print("diagonal") case _: print("outro") # walrus := — atribui e usa na mesma expressão while (linha := f.readline()): processar(linha)
- Não existe
i++nem++i; éi += 1. range(n)vai de0an-1.range(a, b, passo).- Ternário é
a if cond else b(ordem diferente de outras linguagens). - Misturar tabs e espaços →
TabError. Configure o editor para 4 espaços. - Encadear comparações funciona:
0 < x < 10é válido e legível.
✔️ Auto-checagem
O que for...else faz? Diferença entre enumerate e zip. O que o walrus := resolve? Um match não é só um switch — por quê?
Funções
Funções são objetos (podem ser passadas, retornadas, atribuídas). → aprofunde em Python
def f(a, b, /, c, *args, d=10, **kwargs) -> int: """docstring.""" return a + b # a, b → positional-only (antes do /) # c → posicional ou nomeado # *args → tupla dos posicionais extras # d → keyword-only (depois de *), com default # **kwargs → dict dos nomeados extras quadrado = lambda x: x*x # expressão anônima, uma linha só # type hints (não são checados em runtime; use mypy/pyright) def saudar(nome: str, vezes: int = 1) -> str: ... from typing import Optional # ou str | None no 3.10+
Decorators (noção)
@cache # functools.cache — memoiza def fib(n): ... # um decorator é uma função que recebe uma função e retorna outra def logar(fn): def wrapper(*a, **kw): print("chamando", fn.__name__) return fn(*a, **kw) return wrapper
- Default mutável (seção 02):
def f(x=[])— nunca. *args/**kwargsna definição empacotam; na chamada desempacotam.- Type hints não impedem nada em runtime — são para o leitor e o checker.
- Lambda só para expressões triviais; se precisa de
if/statements, usedef. - Toda função retorna algo: sem
return, retornaNone.
✔️ Auto-checagem
O que *args e **kwargs fazem na definição vs na chamada? O que / e * na assinatura significam? O que é um decorator, em uma frase? Type hints são checados em runtime?
Classes
self explícito. Sem private de verdade (convenção _x). → aprofunde em Python
class Conta: taxa = 0.02 # atributo de CLASSE (compartilhado) def __init__(self, saldo=0): self.saldo = saldo # atributo de INSTÂNCIA self._historico = [] # _ = "não mexa" (convenção) def depositar(self, v): self.saldo += v @property def disponivel(self): # acessa como atributo: c.disponivel return self.saldo * (1 - Conta.taxa) @staticmethod def valida(v): return v > 0 # sem self nem cls @classmethod def nova(cls): return cls(100) // factory; cls = a classe def __repr__(self): # o que aparece no debugger/repl return f"Conta(saldo={self.saldo})" class Poupanca(Conta): def __init__(self, saldo=0, rendimento=0.01): super().__init__(saldo) self.rendimento = rendimento
Dataclass (para "sacos de dados")
from dataclasses import dataclass @dataclass class Ponto: x: int y: int = 0 # gera __init__, __repr__, __eq__ de graça
- Esquecer
selfno 1º parâmetro dos métodos. - Atributo de classe mutável (
historico = []no corpo da classe) é compartilhado entre todas as instâncias — coloque no__init__. _xé convenção;__xfaz name-mangling (vira_Classe__x) — não é privacidade real.- Herança múltipla existe e segue a MRO (
Classe.__mro__); use com muita parcimônia. Prefira composição. - Sem
__repr__, o objeto imprime como<Conta object at 0x...>— inútil para debug.
✔️ Auto-checagem
Diferença entre atributo de classe e de instância (e a armadilha do mutável). Quando @staticmethod vs @classmethod? O que @property faz? Para que serve __repr__? Quando usar @dataclass?
Erros, exceções e with
EAFP: "peça perdão, não permissão" — tente e trate a exceção, em vez de checar tudo antes. → aprofunde em Python · FastAPI
try: v = d[chave] except KeyError: v = padrao except (ValueError, TypeError) as e: log.error("falha: %s", e) raise # re-lança o mesmo else: # roda se o try NÃO lançou finally: # SEMPRE roda (cleanup) raise ValueError("mensagem") raise RuntimeError("contexto") from e # encadeia a causa class ValidacaoError(Exception): # exceção custom: herde de Exception pass
Hierarquia (o que você trata)
BaseException → Exception → (ValueError, TypeError, KeyError, IndexError, FileNotFoundError, AttributeError, StopIteration, RuntimeError, …). Capture Exception no máximo — nunca except: pelado (pega KeyboardInterrupt, SystemExit).
with — context managers
with open("dados.txt") as f: # fecha o arquivo mesmo se der erro conteudo = f.read() with lock, open(a) as fa, open(b) as fb: # vários ...
except Exception:silencioso (sem log, sem re-raise) = engolir bug.except:pelado ouexcept BaseException:— captura Ctrl+C e saída do sistema.- Não use exceção para fluxo esperado a ponto de esconder a lógica; mas EAFP idiomático (tentar o
d[k]e tratarKeyError) é ok. - Abrir arquivo/conexão sem
with→ vaza recurso se der erro no meio. raisesozinho dentro doexceptre-lança preservando o traceback;raise NovaEx() from eencadeia.
✔️ Auto-checagem
Quando else e finally num try rodam? Por que nunca except: pelado? O que o with garante? EAFP vs LBYL. Como re-lançar sem perder o traceback?
Módulos, pacotes e ambientes
Um arquivo .py = um módulo. Uma pasta com módulos = um pacote. → aprofunde em Python · Git & GitHub · DevOps/Docker
import json import numpy as np from pathlib import Path from mypkg.util import helper from . import irmao # import relativo (dentro de um pacote) if __name__ == "__main__": # só roda quando o arquivo é executado direto, main() # não quando é importado
Ambiente e dependências
| Passo | Comando |
|---|---|
| Criar ambiente isolado | python -m venv .venv · source .venv/bin/activate (ou uv venv) |
| Instalar | pip install requests · pip install -r requirements.txt · uv pip install ... |
| Congelar | pip freeze > requirements.txt (ou pyproject.toml + lock com uv/Poetry) |
| Rodar | python -m meu_modulo · python script.py |
Nunca instale pacotes no Python do sistema. Sempre um venv por projeto. uv é o gerenciador moderno (rápido; substitui pip/venv/pip-tools).
- Esquecer o
if __name__ == "__main__":→ o código roda ao importar o módulo em outro lugar (e em testes). - Import circular (A importa B que importa A) →
ImportErrorou nome incompleto. Reorganize. from modulo import *polui o namespace — evite.- Rodar sem ativar o venv → usa pacotes errados / do sistema.
- Nome de arquivo igual a um módulo da stdlib (
random.py,json.py) → sombreia a stdlib.
✔️ Auto-checagem
Para que serve if __name__ == "__main__":? Por que um venv por projeto? Diferença entre import x e from x import y. O que quebra num import circular?
Iteração e código "pythonic"
Iterar sobre o objeto, não sobre índices. Preferir comprehension a loop que só constrói lista. → aprofunde em Python · Análise de Dados com Python
Iterável vs iterador vs generator
- Iterável: algo sobre o qual dá para fazer
for(list, str, dict, arquivo…). Tem__iter__. - Iterador: tem
__next__; produz o próximo item; esgota. - Generator: função com
yield— produz valores sob demanda (lazy), sem materializar tudo na memória.
def pares_ate(n): for i in range(0, n, 2): yield i # pausa aqui, devolve i, retoma na próxima for x in pares_ate(1_000_000): # não cria 1M de itens na memória ... # generator expression: some sem construir a lista total = sum(x*x for x in nums)
Idiomas que sinalizam fluência
| Em vez de… | Faça |
|---|---|
for i in range(len(lista)): | for item in lista: ou for i, item in enumerate(lista): |
| construir lista num loop e retornar | comprehension (ou generator se só vai iterar) |
if lista != []: | if lista: |
concatenar string com += num loop | "".join(partes) |
d.keys() só para in | if k in d: |
| abrir/fechar arquivo na mão | with open(...) as f: |
a, b = b, a via temp | a, b = b, a (swap direto) |
| ler o "Zen" | import this — "explícito > implícito", "simples > complexo", "legibilidade conta" |
Bibliotecas para conhecer: itertools (chain, groupby, product, islice), functools (cache, reduce, partial), collections (Counter, defaultdict, deque, namedtuple).
- Um generator esgota: depois de iterar uma vez, está vazio.
list(gen)para materializar. zippara no menor iterável (useitertools.zip_longestse precisar do maior).- Comprehension aninhada difícil de ler = use um loop normal.
range,map,filter,dict.items()são lazy — não são listas; embrulhe comlist()se precisar reusar.
✔️ Auto-checagem
Diferença entre iterável, iterador e generator. O que yield faz? Por que sum(x for x in ...) é melhor que sum([x for x in ...])? Reescreva um for i in range(len(...)) de forma pythonic. O que zip faz quando os iteráveis têm tamanhos diferentes?
O resto do essencial
Itens que caem e você tem que reconhecer. → aprofunde em Python · Análise de Dados com Python · FastAPI
| Tópico | O mínimo |
|---|---|
| f-strings | f"{x:.2f}", f"{x:,}" (milhar), f"{x=}" (debug: imprime x=valor), f"{dt:%Y-%m-%d}" |
| pathlib | Path("dir") / "arquivo.txt", .exists(), .read_text(), .glob("*.csv"). Prefira a os.path |
| json | json.loads(s) / json.dumps(o, indent=2, ensure_ascii=False). Não serializa datetime/set sem default= |
| datetime | datetime.now(tz=...) — sempre com timezone; date, timedelta. Formatar: strftime; parsear: strptime / fromisoformat |
| logging | logging.getLogger(__name__) + logger.info("...", extra=...). Nunca print para diagnóstico em código real |
| typing | list[int], dict[str, int], X | None (3.10+), Optional, Any, Callable`, |
| Números | 1_000_000 (separador), // (divisão inteira), %, **, divmod, round (banker's rounding), decimal.Decimal para dinheiro |
| Strings | .strip(), .split(), .join(), .replace(), .startswith/endswith, .lower/upper, in. str é imutável |
| Mutabilidade & aliasing | Passar list/dict a função e mutá-la afeta o chamador. Copie (.copy(), [:], deepcopy) quando não quer isso |
| Comprehension vs loop | Comprehension para transformar/filtrar; loop quando há efeito colateral ou lógica complexa |
| GIL (1 frase) | Só uma thread executa bytecode Python por vez → threads ajudam em I/O, não em CPU. Para CPU: multiprocessing, concurrent.futures.ProcessPoolExecutor, ou libs em C/Rust |
| async (noção) | async def / await / asyncio.run(main()) / asyncio.gather(...) — concorrência de I/O, single-thread. → FastAPI |
| Testes | pytest: def test_x(): assert f(2) == 4. Rodar pytest -q |
| Estilo | PEP 8; formatadores/linters: ruff (o padrão moderno) / black |
✔️ Auto-checagem
O que f"{x=}" imprime? / vs //. Por que print não substitui logging? O que o GIL implica para threads? Como copiar um dict aninhado sem aliasing? O que é PEP 8 e qual ferramenta aplica?
Checklist final dos fundamentos de Python
Se você marca todos de cabeça, tem o básico. Onde travar, volte à seção — e à apostila completa se a dúvida for de fundo.
- quais tipos são imutáveis; por que
is Nonee nunca== None - a diferença entre
ise==(e por que não confiar emispara inteiros/strings) - o que é falsy em Python e por que
if x:pode ser bug com0/"" - que variável é um nome, que
b = anão copia, e como copiar (raso vsdeepcopy) - a regra LEGB,
global,nonlocal, e a armadilha do default mutável - list / tuple / dict / set — quando cada um, e que
inem set/dict é O(1) - comprehensions (list/dict/set) e generator expressions
- slicing (
a[::-1],a[1:4]), unpacking (a, *rest = ...,{**d1, **d2}) - que
sort()muta e retornaNone;sorted()retorna nova lista - a cilada do
[[0]*3]*3 for...else,enumerate,zip,match(3.10+), walrus:=*args/**kwargs(definição empacota, chamada desempacota),/e*na assinatura- type hints (não são runtime),
lambda, e o que é um decorator self; atributo de classe vs instância (e a armadilha do mutável)@property,@staticmethod,@classmethod,__repr__,@dataclasstry/except/else/finally,raise ... from e, exceção custom- nunca
except:pelado; nunca engolir erro sem log/re-raise withpara arquivos/locks/conexões; EAFPif __name__ == "__main__":; um venv por projeto;uv/pip- iterável vs iterador vs generator;
yield; idiomas pythonic (iterar sobre o objeto,"".join,if lista:) - o GIL em uma frase;
loggingem vez deprint;pathlib;ruff/PEP 8
Aprofundamento por tema: Python (do básico ao avançado, a apostila completa) · Análise de Dados com Python (pandas, numpy) · Algoritmos com Python (estruturas e complexidade) · FastAPI (async, APIs) · Aceleração de Código com IA (fluxo assistido).