Programação do zero: valores, decisões e repetições
Este capítulo ensina programação a partir de uma ordem de serviço, sem presumir experiência anterior. O leitor aprende o que são valor, tipo, variável, expressão, função, condição, repetição, coleção, estado, erro e teste antes de encontrar cada construção em Python. A regra de prioridade de manutenção cresce passo a passo: começa com dados simples, valida uma entrada externa, decide uma classificação, percorre peças e produz um resultado testável. O texto diferencia erro de sintaxe, execução e regra de negócio; mostra como diagnosticar uma falha provocada; relaciona tipos internos a validação de fronteira; e introduz complexidade, núcleo puro e segurança de entrada sem transformar o primeiro programa numa caixa-preta.
Programação do zero: valores, decisões e repetições
O problema antes do código
Imagine uma ordem de serviço, ou OS, aberta porque um gerador apresentou superaquecimento. O sistema recebeu criticidade 4, indicação de risco à vida e três peças utilizadas. Ele precisa verificar os dados, classificar a prioridade e calcular a quantidade total de peças. Um ser humano pode explicar a regra assim:
- rejeite criticidade fora de 1 a 5;
- se houver risco à vida, a prioridade é imediata;
- senão, se a criticidade for 4 ou 5, a prioridade é alta;
- nos demais casos, a prioridade é normal;
- some as quantidades das peças.
Essa sequência finita e não ambígua é um algoritmo. Um programa é a representação executável desse algoritmo numa linguagem. Python será usado porque permite enxergar a regra com pouco ruído; os conceitos também existem em TypeScript, Java e outras linguagens.
O vocabulário mínimo
Um valor é um dado concreto: 4, True ou "Gerador". Um tipo classifica valores e determina operações possíveis. 4 é inteiro; True é booleano — verdadeiro ou falso —; "Gerador" é texto. Somar 4 + 2 faz sentido; somar "Gerador" + 2 não descreve uma operação definida por Python.
Uma variável associa um nome a um valor. Em criticidade = 4, o nome criticidade passa a referir-se ao inteiro 4. O símbolo = aqui significa atribuição, não uma pergunta matemática de igualdade. Uma expressão combina valores e operações e produz outro valor: criticidade >= 4 produz True ou False.
equipamento = "Gerador" # texto: str
criticidade = 4 # inteiro: int
risco_vida = True # booleano: bool
prioridade_alta = criticidade >= 4 or risco_vidaLeia a última linha da esquerda para a direita: compare a criticidade com 4; combine o resultado com o valor de risco_vida usando or (“ou”); associe o booleano final ao nome prioridade_alta. Os comentários após # explicam o código e não são executados.
O conjunto de valores relevantes em determinado momento é o estado do programa. Se depois executarmos criticidade = 5, o estado mudou: o mesmo nome passou a referir-se a outro valor. Essa alteração é uma mutação. Estado é necessário, mas muitas alterações espalhadas tornam difícil responder “quem mudou isso e quando?”.
Função: uma transformação com nome
Uma função reúne uma operação reutilizável. Ela recebe parâmetros, nomes disponíveis dentro dela, e pode produzir um retorno. O cabeçalho abaixo promete receber criticidade e risco; o bloco recuado contém a regra:
def classificar_prioridade(criticidade, risco_vida):
if risco_vida:
return "imediata"
if criticidade >= 4:
return "alta"
return "normal"Linha a linha:
defcomeça a definição; nada é classificado ainda.classificar_prioridadeé o nome escolhido para revelar a intenção.- os valores serão recebidos pelos parâmetros entre parênteses;
ifsignifica “se”. Seu bloco executa somente quando a condição é verdadeira;returnencerra a função e entrega um valor ao chamador;- se as duas condições anteriores forem falsas, o último retorno produz
"normal".
Uma chamada executa a função: classificar_prioridade(4, False) retorna "alta". O valor 4 ocupa o parâmetro criticidade; False ocupa risco_vida. O recuo não é decoração: em Python, ele delimita o bloco. A documentação oficial apresenta essas construções no tutorial de fluxo de controle.
O contrato da função inclui mais que seu nome. Devemos decidir: quais valores aceita, o que retorna e como sinaliza entrada inválida. A função atual aceita acidentalmente 999. O código executa, porém a regra do negócio fica errada. Tipos ajudam a explicar intenção, mas uma entrada externa ainda precisa ser validada.
Coleção e repetição: trabalhar com vários valores
Uma coleção agrupa valores. Uma lista preserva sequência e pode conter repetição. Cada peça da OS pode ser representada por um dicionário, coleção de pares chave–valor:
pecas = [
{"codigo": "FUS-10", "quantidade": 2},
{"codigo": "CAB-02", "quantidade": 1},
]Os colchetes externos formam a lista; cada par de chaves forma um dicionário. A expressão pecas[0] acessa o primeiro item — índices começam em zero. A expressão pecas[0]["quantidade"] produz 2.
Uma repetição, também chamada laço, executa um bloco para vários itens:
def totalizar_pecas(pecas):
total = 0
for peca in pecas:
total = total + peca["quantidade"]
return totalAntes do laço, total vale zero. Em cada volta, peca representa o próximo dicionário e a expressão à direita calcula um novo total. Depois de duas voltas, os estados de total foram 0, 2 e 3. Finalmente, a função retorna 3.
Não confunda lista com conjunto. Um conjunto, set em Python, é adequado quando a pergunta dominante é “este valor já apareceu?” e não precisamos representar duplicatas. Um dicionário é adequado quando procuramos um valor por chave. A documentação de tipos nativos descreve as garantias dessas estruturas.
Entrada externa: texto não é confiança
Dados vindos de tela, arquivo, API — interface pela qual programas conversam — ou ERP — sistema integrado de gestão empresarial — estão fora do controle do núcleo. Mesmo que uma tela limite criticidade a 1–5, alguém pode chamar a API diretamente, uma versão antiga pode enviar outro formato ou uma integração pode estar defeituosa. A OWASP — Open Worldwide Application Security Project, comunidade que publica orientações de segurança, recomenda validar cedo tanto a forma quanto o significado, usando uma lista positiva do que é aceito (Input Validation Cheat Sheet).
def ler_criticidade(valor):
if type(valor) is not int:
raise ValueError("criticidade deve ser um inteiro")
if valor < 1 or valor > 5:
raise ValueError("criticidade deve estar entre 1 e 5")
return valorraise interrompe o caminho normal e produz uma exceção, um objeto que comunica falha. A primeira condição valida o tipo. A segunda valida o domínio permitido. Usamos type(valor) is not int intencionalmente porque, em Python, bool deriva de int; isinstance(True, int) seria verdadeiro, embora True não deva significar criticidade 1.
Validar não é autorizar. 4 pode ser uma criticidade válida, mas o usuário ainda pode não ter permissão para alterar aquela OS. Também não se deve concatenar uma entrada validada parcialmente numa consulta SQL — Structured Query Language, linguagem usada com bancos relacionais — ou comando de sistema; use parâmetros e APIs próprias da biblioteca. Limite ainda quantidade de itens e tamanho de texto para impedir consumo desproporcional de memória e processamento.
Núcleo puro e borda impura
Uma função pura depende apenas dos argumentos, não altera algo externo e, para a mesma entrada, devolve o mesmo resultado. classificar_prioridade pode ser pura. Gravar no ERP, ler relógio ou imprimir são efeitos colaterais, mudanças observáveis além do retorno.
Uma arquitetura simples separa:
entrada externa → validação → regra pura → gravação/saídaIsso não elimina efeitos; concentra-os nas bordas. A regra pode ser testada sem ERP, rede ou banco. Se amanhã a entrada deixar de vir do terminal e passar a vir por HTTP, a classificação não deveria mudar. Se mudar, transporte e negócio provavelmente estão acoplados.
Erro não é uma coisa só
Há três falhas iniciais importantes:
- Erro de sintaxe: o texto não forma um programa válido, como esquecer
:depois deif. Python aponta arquivo e linha antes de executar a regra. - Erro de execução: o programa começou, mas encontrou uma operação impossível naquele caminho, como acessar a chave inexistente
peca["quantidades"]. A mensagem e o traceback mostram a sequência de chamadas. - Erro lógico: o programa termina, mas entrega a resposta errada, como classificar criticidade 4 como normal. Só especificação, exemplo ou teste revela a divergência.
Diagnostique sem trocar código ao acaso: reproduza com a menor entrada; leia a última linha do erro; encontre a primeira linha do seu código no traceback; inspecione valores e tipos; formule uma hipótese; altere uma coisa; execute novamente. A referência oficial explica a anatomia de erros e exceções.
Falha provocada:
def totalizar_pecas_com_falha(pecas):
total = 0
for peca in pecas:
total = peca["quantidade"] # substitui em vez de acumular
return totalCom quantidades 2 e 1, o resultado é 1, não 3. Não há exceção: é erro lógico. Uma tabela de rastreamento evidencia o problema.
| Momento | total correto |
total com falha |
|---|---|---|
| início | 0 | 0 |
| depois da primeira peça | 2 | 2 |
| depois da segunda | 3 | 1 |
A correção é total = total + peca["quantidade"]. O teste impede o retorno acidental da falha.
Teste: exemplo que decide sozinho
Um teste automatizado prepara uma entrada, executa uma unidade e compara observado com esperado. assert significa “esta condição precisa ser verdadeira”:
assert classificar_prioridade(4, False) == "alta"
assert classificar_prioridade(1, True) == "imediata"
assert totalizar_pecas([]) == 0
assert totalizar_pecas([{"codigo": "F", "quantidade": 2}]) == 2Esses testes cobrem decisões e a fronteira vazia. Ainda precisamos testar criticidades 1 e 5, tipo inválido, quantidade negativa e coleção excessiva. A biblioteca unittest organiza casos maiores e seus resultados na documentação oficial.
Custo: quanto o trabalho cresce
Se uma função percorre uma lista uma vez, o número de passos cresce aproximadamente junto com a entrada: chamamos isso de O(n), crescimento linear. Se para cada item ela percorre a lista inteira, o crescimento tende a O(n²). Essa complexidade assintótica não significa segundos e não prevê constantes, máquina ou tamanho pequeno. Uma solução O(n²) pode vencer para dez itens; em escala, sua curva cresce mais depressa.
Para detectar códigos duplicados, lista.count(codigo) dentro de um laço repete buscas e tende a O(n²). Guardar códigos já vistos num conjunto exige memória extra, mas oferece pertencimento com custo O(1) esperado — aproximadamente constante — e uma passagem O(n). O requisito decide: se a ordem da primeira duplicata importa, combine conjunto para busca e lista para saída. O laboratório mede as duas alternativas sem transformar “conjunto é melhor” em dogma.
Critérios de aceite e prática
Você dominou esta etapa quando consegue, sem copiar:
- explicar cada termo destacado com um exemplo diferente de OS;
- prever o retorno de uma função e registrar o estado a cada repetição;
- validar tipo, faixa e tamanho antes da regra;
- escrever testes de caminho feliz, fronteira e entrada inválida;
- distinguir sintaxe, execução e lógica a partir de evidência;
- trocar a borda terminal por HTTP, protocolo usado na comunicação web, sem modificar o núcleo da regra.
Exercício: modele uma OS com horas_parada, risco_vida e uma lista de peças. Escreva ler_horas_parada, classificar_prioridade e totalizar_pecas. Provoque uma falha trocando + por -, capture o teste que falha, corrija e registre antes/depois. Como transferência, use a mesma estrutura para triagem de chamados de TI: quais nomes mudam e quais conceitos permanecem?
Recuperação ativa: feche o capítulo e responda: um valor e uma variável são a mesma coisa? O que uma expressão produz? Quando um if não executa? Qual estado muda num laço? Por que tipo interno não substitui validação externa? Qual diferença entre erro de execução e lógico? Por que O(n) não quer dizer “um segundo”?
Continue no caderno operacional, onde você executará, quebrará, diagnosticará e medirá um programa completo.
Comprove o que você aprendeu
Responda todas as questões. O gabarito comentado só aparece depois do envio.