Continuação da introdução à programação usando a linguagem Python.
def para modularizar e reutilizar código, a compreender o escopo de variáveis (local e global) e, por fim, a dar os primeiros passos no versionamento de código com Git — uma habilidade indispensável para qualquer programador moderno. Com esses recursos, seus programas deixarão de ser scripts monolíticos e se tornarão projetos organizados, legíveis e prontos para crescer.
Um dos maiores desafios enfrentados por programadores iniciantes não é aprender a sintaxe de uma linguagem, mas sim aprender a pensar como um programador. Diante de um problema complexo, a tentação natural é tentar resolvê-lo de uma vez. Programadores experientes, porém, fazem o oposto: eles decompõem o problema em partes menores, mais simples e mais fáceis de resolver individualmente (CORMEN et al., 2009).
Vejamos um exemplo concreto. Suponha que você precise criar um programa que leia as notas de uma turma, calcule a média, identifique a maior e a menor nota e exiba um relatório. Em vez de escrever tudo em um bloco único, podemos decompor o problema:
Subproblemas identificados:
1. Ler as notas dos alunos (entrada de dados).
2. Calcular a média aritmética (processamento).
3. Encontrar a maior e a menor nota (processamento).
4. Determinar quantos foram aprovados e reprovados (processamento).
5. Exibir o relatório formatado (saída de dados).
Cada um desses subproblemas pode ser implementado como uma função independente — conceito que estudaremos a partir da seção 3.
# ❌ ABORDAGEM MONOLÍTICA (tudo junto — difícil de ler e manter)
notas = []
n = int(input("Quantos alunos? "))
for i in range(n):
nota = float(input(f"Nota do aluno {i+1}: "))
notas.append(nota)
soma = 0
for nota in notas:
soma += nota
media = soma / n
maior = notas[0]
menor = notas[0]
for nota in notas:
if nota > maior: maior = nota
if nota < menor: menor = nota
aprov = 0
for nota in notas:
if nota >= 7: aprov += 1
print(f"Média: {media:.1f} | Maior: {maior} | Menor: {menor}")
print(f"Aprovados: {aprov} | Reprovados: {n - aprov}")
Esse código funciona, mas é difícil de ler, testar e modificar. Na seção 3, veremos como transformá-lo em algo muito mais organizado usando funções.
A decomposição é um dos quatro pilares do pensamento computacional, uma abordagem para resolução de problemas proposta pela cientista da computação Jeannette Wing. Segundo (WING, 2006), o pensamento computacional é uma habilidade fundamental que deveria ser ensinada a todas as pessoas, não apenas a cientistas da computação, pois envolve uma forma de raciocinar que se aplica a qualquer campo do conhecimento.
Ao programar em Python, a decomposição se materializa na criação de funções; o reconhecimento de padrões leva à reutilização de código; a abstração permite que criemos funções genéricas que funcionam para diferentes entradas; e os algoritmos são a lógica que implementamos dentro de cada função (MENEZES, 2019).
# Exemplo: Calcular a média de QUALQUER lista de números
# Aplicando os 4 pilares:
# 1. Decomposição: separar leitura, cálculo e exibição
# 2. Padrão: o cálculo de média é sempre soma/quantidade
# 3. Abstração: a função não depende de QUAIS números são
# 4. Algoritmo: somar tudo, dividir pela quantidade
# Passo 1: Leitura (decomposição)
valores = []
n = int(input("Quantos valores? "))
for i in range(n):
v = float(input(f"Valor {i + 1}: "))
valores.append(v)
# Passo 2: Cálculo — padrão identificado (algoritmo)
soma = sum(valores) # sum() soma todos os itens
media = soma / len(valores) # len() conta os itens
# Passo 3: Exibição (decomposição)
print(f"Soma: {soma:.2f}")
print(f"Média: {media:.2f}")
Uma função é um bloco de código nomeado e reutilizável que realiza uma tarefa específica. Funções são a principal ferramenta de modularização em programação: permitem dividir um programa extenso em módulos menores, independentes e fáceis de entender, testar e manter (MATTHES, 2019).
Em Python, já utilizamos diversas funções embutidas (built-in) como print(), input(), int(), float(), len() e range(). Agora, aprenderemos a criar nossas próprias funções usando a palavra reservada def.
# Definindo uma função com def
def saudacao():
"""Exibe uma mensagem de boas-vindas."""
print("Olá! Bem-vindo ao programa.")
print("Vamos aprender sobre funções!")
# Neste ponto, a função foi DEFINIDA, mas ainda NÃO executada.
# Para executá-la, precisamos CHAMÁ-LA:
saudacao() # Chamada 1
print("---")
saudacao() # Chamada 2 (reutilização!)
Olá! Bem-vindo ao programa.
Vamos aprender sobre funções!
---
Olá! Bem-vindo ao programa.
Vamos aprender sobre funções!
def) com a sua execução (chamada com parênteses). A definição apenas registra a função na memória. Para que ela seja executada, é preciso chamá-la pelo nome seguido de parênteses: saudacao(). Sem os parênteses, Python apenas referencia o objeto da função, sem executá-la (SWEIGART, 2020).
As funções se tornam muito mais poderosas quando podem receber dados de entrada. Esses dados são chamados de parâmetros (na definição da função) e argumentos (na chamada da função). Essa distinção é sutil, mas importante: o parâmetro é a variável na assinatura da função, enquanto o argumento é o valor concreto passado na chamada (MATTHES, 2019).
# Função com um parâmetro
def saudacao_pessoal(nome):
"""Exibe uma saudação personalizada."""
print(f"Olá, {nome}! Seja bem-vindo(a)!")
saudacao_pessoal("Ana") # Olá, Ana! Seja bem-vindo(a)!
saudacao_pessoal("Carlos") # Olá, Carlos! Seja bem-vindo(a)!
# Função com múltiplos parâmetros
def apresentar(nome, idade, curso):
"""Apresenta um aluno com seus dados."""
print(f"Nome: {nome}")
print(f"Idade: {idade} anos")
print(f"Curso: {curso}")
print()
apresentar("Maria", 22, "Engenharia")
apresentar("João", 19, "Ciência da Computação")
Podemos definir valores padrão para parâmetros. Se o argumento não for fornecido na chamada, o valor padrão é utilizado (PYTHON SOFTWARE FOUNDATION, 2024).
# Parâmetro com valor padrão (default)
def potencia(base, expoente=2):
"""Calcula base elevada ao expoente (padrão: ao quadrado)."""
resultado = base ** expoente
print(f"{base}^{expoente} = {resultado}")
potencia(5) # 5^2 = 25 (usa o padrão expoente=2)
potencia(5, 3) # 5^3 = 125 (sobrescreve o padrão)
potencia(2, 10) # 2^10 = 1024
# Outro exemplo: formatação de preço
def formatar_preco(valor, moeda="R$", casas=2):
"""Formata um valor monetário."""
print(f"{moeda} {valor:.{casas}f}")
formatar_preco(49.9) # R$ 49.90
formatar_preco(49.9, "US$") # US$ 49.90
formatar_preco(49.9, "€", 0) # € 50
# Argumentos nomeados: a ordem não importa
def ficha(nome, idade, cidade):
print(f"{nome}, {idade} anos, mora em {cidade}.")
# Chamada posicional (ordem importa)
ficha("Ana", 25, "Brasília")
# Chamada com argumentos nomeados (ordem NÃO importa)
ficha(cidade="Natal", nome="Bruno", idade=30)
# Misturando posicionais e nomeados
ficha("Carla", cidade="Recife", idade=22)
returnAté agora, nossas funções apenas exibiam resultados com print(). No entanto, na maioria dos casos, queremos que a função calcule um valor e o devolva ao código que a chamou, para que esse valor possa ser armazenado, combinado ou usado em outras operações. Para isso, usamos a instrução return (SWEIGART, 2020).
print() apenas exibe um valor na tela — o valor é mostrado e "desaparece". return devolve o valor ao código chamador, permitindo que ele seja armazenado em uma variável e reutilizado. Uma função que usa print() sem return retorna implicitamente None. Esta é uma das confusões mais comuns entre iniciantes (MATTHES, 2019).
# ❌ Função que apenas EXIBE (não reutilizável)
def soma_print(a, b):
print(a + b)
resultado = soma_print(3, 4) # Exibe 7 na tela
print(resultado) # None! (não retornou nada)
# ✅ Função que RETORNA (reutilizável)
def soma(a, b):
return a + b
resultado = soma(3, 4) # Armazena 7 na variável
print(resultado) # 7
print(soma(10, 20) * 2) # 60 (podemos operar com o retorno!)
print(soma(1, 2) + soma(3, 4)) # 10 (composição de chamadas)
Python permite que uma função retorne múltiplos valores de uma só vez, separados por vírgula. Internamente, Python empacota esses valores em uma tupla (PYTHON SOFTWARE FOUNDATION, 2024).
# Retornando múltiplos valores
def estatisticas(numeros):
"""Calcula soma, média, maior e menor de uma lista."""
total = sum(numeros)
media = total / len(numeros)
maior = max(numeros)
menor = min(numeros)
return total, media, maior, menor
# Chamando e desempacotando os valores
notas = [8.5, 6.0, 9.2, 7.3, 5.8]
s, m, mx, mn = estatisticas(notas)
print(f"Soma: {s:.1f}") # 36.8
print(f"Média: {m:.1f}") # 7.4
print(f"Maior: {mx}") # 9.2
print(f"Menor: {mn}") # 5.8
Agora, podemos reescrever o programa monolítico da seção 1 de forma modular. Compare a clareza do código:
# ✅ ABORDAGEM MODULAR (decomposta em funções)
def ler_notas():
"""Lê as notas dos alunos e retorna uma lista."""
notas = []
n = int(input("Quantos alunos? "))
for i in range(n):
nota = float(input(f"Nota do aluno {i + 1}: "))
notas.append(nota)
return notas
def calcular_media(lista):
"""Retorna a média aritmética de uma lista de números."""
return sum(lista) / len(lista)
def contar_aprovados(lista, nota_corte=7.0):
"""Conta quantos valores atingem a nota de corte."""
aprovados = 0
for nota in lista:
if nota >= nota_corte:
aprovados += 1
return aprovados
def exibir_relatorio(lista):
"""Exibe o relatório completo da turma."""
media = calcular_media(lista)
aprov = contar_aprovados(lista)
reprov = len(lista) - aprov
print(f"\n{'='*35}")
print(f" RELATÓRIO DA TURMA")
print(f"{'='*35}")
print(f" Alunos: {len(lista)}")
print(f" Média: {media:.1f}")
print(f" Maior nota: {max(lista)}")
print(f" Menor nota: {min(lista)}")
print(f" Aprovados: {aprov}")
print(f" Reprovados: {reprov}")
print(f"{'='*35}")
# === Programa principal (claro e legível) ===
notas = ler_notas()
exibir_relatorio(notas)
2. Reutilização:
calcular_media() pode ser usada em qualquer programa que precise de média.3. Testabilidade: cada função pode ser testada isoladamente.
4. Manutenção: para alterar a nota de corte, basta modificar
contar_aprovados().
Ao criar funções, surge uma questão fundamental: onde uma variável pode ser acessada? A resposta está no conceito de escopo. O escopo de uma variável define a região do código em que ela é visível e pode ser utilizada. Python possui dois escopos principais: local e global (SWEIGART, 2020).
Variáveis criadas dentro de uma função (incluindo seus parâmetros) possuem escopo local: elas existem apenas durante a execução daquela função e são destruídas quando a função termina. Não é possível acessá-las de fora da função (PAIVA, 2021).
# Escopo local: variáveis dentro da função
def calcular():
resultado = 10 + 20 # 'resultado' é LOCAL
print(f"Dentro: {resultado}") # 30
calcular()
# Tentar acessar 'resultado' aqui causará ERRO:
# print(resultado) # NameError: name 'resultado' is not defined
# -----------------------------------------------
# Variáveis locais de funções diferentes são INDEPENDENTES
def funcao_a():
x = 10
print(f"funcao_a: x = {x}")
def funcao_b():
x = 99 # Este 'x' é OUTRO, diferente do 'x' de funcao_a
print(f"funcao_b: x = {x}")
funcao_a() # funcao_a: x = 10
funcao_b() # funcao_b: x = 99
Variáveis criadas fora de qualquer função, no nível principal do programa, possuem escopo global: elas podem ser lidas de dentro de qualquer função. Porém, para modificar uma variável global dentro de uma função, é necessário usar a palavra-chave global — embora essa prática seja geralmente desaconselhada (SWEIGART, 2020).
# Escopo global: variável acessível em todo o programa
PI = 3.14159 # Variável GLOBAL (convenção: MAIÚSCULAS para constantes)
def area_circulo(raio):
"""Calcula a área usando o PI global."""
return PI * raio ** 2 # Lê PI sem problema
print(area_circulo(5)) # 78.53975
# -----------------------------------------------
# ⚠ Modificar global dentro de função (EVITE!)
contador = 0
def incrementar():
global contador # Declara intenção de modificar a global
contador += 1
incrementar()
incrementar()
print(contador) # 2
# ✅ MELHOR PRÁTICA: usar parâmetros e return
def incrementar_v2(valor):
return valor + 1
contador = 0
contador = incrementar_v2(contador)
contador = incrementar_v2(contador)
print(contador) # 2 (mesmo resultado, sem efeito colateral)
return, mantendo as funções puras e previsíveis. Constantes globais (como PI), por outro lado, são perfeitamente aceitáveis, pois nunca são modificadas (MATTHES, 2019).
Python resolve nomes de variáveis seguindo a regra LEGB (Local → Enclosing → Global → Built-in): procura primeiro no escopo local, depois no escopo de funções envolventes (para funções aninhadas), depois no escopo global e, por fim, entre os nomes embutidos da linguagem (PYTHON SOFTWARE FOUNDATION, 2024).
# Demonstração da regra LEGB
x = "global" # (G) Global
def externa():
x = "enclosing" # (E) Enclosing (envolvente)
def interna():
x = "local" # (L) Local
print(f"Interna vê: {x}")
interna()
print(f"Externa vê: {x}")
externa()
print(f"Global vê: {x}")
# Saída:
# Interna vê: local
# Externa vê: enclosing
# Global vê: global
Criar funções que funcionem é o primeiro passo. Criar funções bem escritas é o que diferencia um programador iniciante de um intermediário. A seguir, apresentamos as principais convenções e boas práticas adotadas pela comunidade Python (PYTHON SOFTWARE FOUNDATION, 2024).
| Prática | Descrição | Exemplo |
|---|---|---|
| Nomes descritivos | Use verbos que indiquem a ação; estilo snake_case |
calcular_media, validar_cpf |
| Função = 1 tarefa | Cada função deve resolver um problema específico | Separar leitura, cálculo e exibição |
| Docstrings | Documente o propósito com """aspas triplas""" |
"""Retorna a média de uma lista.""" |
| Parâmetros claros | Use nomes significativos para os parâmetros | raio em vez de r |
| Evite efeitos colaterais | Prefira return a modificar variáveis globais |
Retornar o resultado em vez de alterar estado externo |
| Tamanho moderado | Se a função é muito longa, decomponha-a | Limite de ~20 linhas como referência |
# ❌ MAU EXEMPLO
def f(l):
s = 0
for i in l:
s += i
print(s / len(l))
# ✅ BOM EXEMPLO
def calcular_media(notas):
"""Retorna a média aritmética de uma lista de notas.
Parâmetros:
notas (list): Lista de valores numéricos.
Retorna:
float: A média aritmética.
"""
if not notas:
return 0.0
return sum(notas) / len(notas)
# Usando
resultado = calcular_media([8.0, 7.5, 9.0])
print(f"Média: {resultado:.1f}") # 8.2
# Acessando a docstring:
help(calcular_media)
snake_case para nomes de funções e variáveis. A PEP 257 define as convenções para docstrings. Seguir essas convenções torna seu código consistente com o ecossistema Python e mais fácil de ser lido por outros programadores (PYTHON SOFTWARE FOUNDATION, 2024).
À medida que seus programas crescem, surge um problema prático: como manter o controle das alterações feitas no código ao longo do tempo? Como voltar a uma versão anterior que funcionava? Como colaborar com outras pessoas sem sobrescrever o trabalho umas das outras? A resposta para todas essas perguntas é o versionamento de código.
Imagine que você está escrevendo um programa e faz uma alteração que quebra tudo. Sem versionamento, sua única opção seria tentar lembrar o que mudou e desfazer manualmente. Com versionamento, basta voltar ao estado anterior com um comando. A ferramenta mais utilizada no mundo para essa tarefa é o Git (CHACON; STRAUB, 2014).
| Benefício | Descrição |
|---|---|
| Histórico completo | Cada alteração é registrada com data, autor e descrição |
| Reversão segura | Voltar a qualquer versão anterior a qualquer momento |
| Colaboração | Múltiplas pessoas podem trabalhar no mesmo projeto simultaneamente |
| Ramificação (branches) | Criar versões paralelas para experimentar sem afetar o código principal |
| Padrão da indústria | Git é requisito em praticamente toda vaga de programação |
O Git é um sistema de controle de versão distribuído, criado por Linus Torvalds em 2005 (o mesmo criador do Linux). Ele funciona rastreando as alterações em arquivos e permitindo que múltiplas versões do projeto coexistam. O Git é utilizado em conjunto com plataformas de hospedagem como GitHub, GitLab e Bitbucket (CHACON; STRAUB, 2014).
| Comando | Função | Exemplo |
|---|---|---|
git init |
Inicializa um repositório Git no diretório atual | git init |
git status |
Mostra o estado atual dos arquivos | git status |
git add |
Adiciona arquivos à staging area | git add programa.py |
git commit |
Registra as alterações no histórico local | git commit -m "Adiciona função de média" |
git log |
Exibe o histórico de commits | git log --oneline |
git diff |
Mostra as diferenças desde o último commit | git diff |
git push |
Envia commits para o repositório remoto | git push origin main |
git pull |
Traz alterações do repositório remoto | git pull origin main |
git clone |
Copia um repositório remoto para o computador | git clone https://github.com/user/repo.git |
# 1. Criar uma pasta e inicializar o Git
mkdir meu_projeto
cd meu_projeto
git init
# 2. Criar um arquivo Python
echo 'print("Olá, Git!")' > programa.py
# 3. Verificar o status (arquivo aparece como "untracked")
git status
# 4. Adicionar o arquivo à staging area
git add programa.py
# 5. Registrar a alteração (commit)
git commit -m "Primeiro commit: programa inicial"
# 6. Ver o histórico
git log --oneline
# a1b2c3d Primeiro commit: programa inicial
Embora o Google Colab não possua o Git integrado de forma visual, é possível utilizar comandos Git diretamente nas células do Colab usando o prefixo ! (que executa comandos do terminal). Isso permite clonar repositórios, salvar seu trabalho no GitHub e colaborar com colegas sem sair do navegador (CHACON; STRAUB, 2014).
# Clonar um repositório público do GitHub
!git clone https://github.com/usuario/meu-projeto.git
# Entrar na pasta do projeto
%cd meu-projeto
# Verificar os arquivos
!ls -la
# Ver o histórico de commits
!git log --oneline -5
# 1. Configurar identidade (necessário na primeira vez)
!git config --global user.name "Seu Nome"
!git config --global user.email "seu@email.com"
# 2. Verificar alterações
!git status
# 3. Adicionar e fazer commit
!git add .
!git commit -m "Atualiza funções do exercício 3"
# 4. Enviar para o GitHub (requer autenticação)
# Para repositórios privados, use um Personal Access Token:
!git push https://<TOKEN>@github.com/usuario/repo.git main
from google.colab import userdata; token = userdata.get('GITHUB_TOKEN').
Além do Git, o próprio Google Colab oferece um sistema de versionamento embutido. No menu Arquivo → Histórico de versões (ou Ctrl + Shift + H), é possível ver todas as versões salvas automaticamente do notebook, comparar diferenças e restaurar versões anteriores. Para projetos simples e trabalhos individuais, essa funcionalidade pode ser suficiente.
Git + GitHub: ideal para projetos maiores, colaboração em equipe, portfólio profissional e quando você precisa de controle preciso sobre cada alteração.
Para consolidar todos os conceitos desta Parte III — decomposição, funções com parâmetros e retorno, escopo de variáveis e boas práticas —, vamos construir um sistema de gerenciamento de notas completo e modular:
# ================================================
# Sistema de Notas - Exemplo Integrador (Parte III)
# Conceitos: decomposição, def, parâmetros, return,
# escopo, docstrings, boas práticas
# ================================================
# --- Constantes globais ---
NOTA_APROVACAO = 7.0
NOTA_RECUPERACAO = 5.0
# --- Funções de entrada ---
def ler_nota(mensagem):
"""Lê uma nota válida (0 a 10) com tratamento de erro."""
while True:
try:
nota = float(input(mensagem))
if 0 <= nota <= 10:
return nota
print(" A nota deve estar entre 0 e 10.")
except ValueError:
print(" Entrada inválida! Digite um número.")
def ler_notas_turma():
"""Lê as notas de todos os alunos da turma."""
n = int(input("Quantos alunos na turma? "))
turma = []
for i in range(1, n + 1):
nome = input(f"Nome do aluno {i}: ")
nota = ler_nota(f" Nota de {nome}: ")
turma.append({"nome": nome, "nota": nota})
return turma
# --- Funções de processamento ---
def calcular_media(turma):
"""Retorna a média das notas da turma."""
notas = [aluno["nota"] for aluno in turma]
return sum(notas) / len(notas) if notas else 0
def classificar_aluno(nota):
"""Retorna a situação do aluno com base na nota."""
if nota >= NOTA_APROVACAO:
return "Aprovado"
elif nota >= NOTA_RECUPERACAO:
return "Recuperação"
else:
return "Reprovado"
def contar_situacoes(turma):
"""Conta aprovados, em recuperação e reprovados."""
contagem = {"Aprovado": 0, "Recuperação": 0, "Reprovado": 0}
for aluno in turma:
situacao = classificar_aluno(aluno["nota"])
contagem[situacao] += 1
return contagem
# --- Funções de saída ---
def exibir_relatorio(turma):
"""Exibe o relatório completo da turma."""
media = calcular_media(turma)
contagem = contar_situacoes(turma)
notas = [a["nota"] for a in turma]
print(f"\n{'='*45}")
print(f"{'RELATÓRIO DA TURMA':^45}")
print(f"{'='*45}")
print(f"{'Nome':<20} {'Nota':>6} {'Situação':<15}")
print(f"{'-'*45}")
for aluno in turma:
sit = classificar_aluno(aluno["nota"])
print(f"{aluno['nome']:<20} {aluno['nota']:>6.1f} {sit:<15}")
print(f"{'-'*45}")
print(f" Média da turma: {media:.1f}")
print(f" Maior nota: {max(notas):.1f}")
print(f" Menor nota: {min(notas):.1f}")
print(f" Aprovados: {contagem['Aprovado']}")
print(f" Recuperação: {contagem['Recuperação']}")
print(f" Reprovados: {contagem['Reprovado']}")
print(f"{'='*45}")
# === PROGRAMA PRINCIPAL ===
turma = ler_notas_turma()
exibir_relatorio(turma)
Nesta Parte III, elevamos significativamente o nível de organização e qualidade dos nossos programas. Começamos compreendendo a importância da decomposição de problemas e do pensamento computacional, habilidades que transcendem qualquer linguagem de programação. Em seguida, aprendemos a criar funções customizadas com def — com parâmetros posicionais, valores padrão, argumentos nomeados e retorno de valores via return.
O estudo do escopo de variáveis (local, global e a regra LEGB) nos mostrou como Python organiza os nomes internamente, e por que devemos evitar variáveis globais modificáveis em favor de funções puras. As boas práticas da PEP 8 e PEP 257 nos orientam a escrever código Pythônico, legível e profissional.
Finalmente, demos os primeiros passos no versionamento de código com Git, compreendendo o fluxo add → commit → push e como utilizar o Git diretamente no Google Colab. Essa habilidade é indispensável para qualquer programador que deseje trabalhar de forma profissional e colaborativa.
O próximo passo natural nesta jornada é explorar as estruturas de dados compostas (listas, tuplas, dicionários e conjuntos), que ampliarão enormemente a capacidade dos seus programas de lidar com coleções de dados do mundo real.
Fonte:
Visto no Brasil Acadêmico


Comentários