UC00618 · Criar aplicações em Python
- Introdução
- 1. Por que Python
- 2. Setup
- 3. Sintaxe básica
- 4. Estruturas de controlo
- 5. Funções
- 6. Estruturas de dados
- 7. OOP — Programação Orientada a Objectos
- 8. Bibliotecas essenciais
- 9. Boas práticas (PEP 8)
- 10. Projecto exemplo
- Apêndice A · PEP 20 — Zen of Python
- Apêndice B · Recursos
- Apêndice C · Glossário
Introdução
Python é a linguagem de programação mais popular do mundo (TIOBE Index, Stack Overflow surveys). Criada por Guido van Rossum em 1991, é hoje usada por: - Google, Instagram, Spotify, Netflix (web e infrastructure). - NASA, CERN (computing científico). - OpenAI, DeepMind (machine learning). - Milhares de startups (rapid prototyping).
Vantagens: - Sintaxe limpa (lê-se quase como inglês). - Versatilidade (web, data, AI, automation, scripts). - Comunidade massiva (bibliotecas para tudo). - Curva de aprendizagem suave. - Salários competitivos.
Esta UC (50h) cobre fundamentos sólidos até projecto prático real. Foco em escrever código Python idiomático (Pythonic).
1. Por que Python
1.1 História e filosofia
- 1991: Guido van Rossum cria Python.
- Nome inspirado em "Monty Python's Flying Circus".
- Filosofia (PEP 20 — Zen of Python):
- Beautiful is better than ugly.
- Explicit is better than implicit.
- Simple is better than complex.
- Readability counts.
- There should be one obvious way to do it.
Ler na REPL: import this.
1.2 Características
- Interpretada: não precisa compilar (rápido para iterar).
- Dinamicamente tipada: tipos verificados em runtime.
- Multi-paradigma: imperativa, OOP, funcional.
- Multi-plataforma: Windows, Mac, Linux, mobile.
- Open-source.
- Standard library rica (vem com tudo).
- Garbage collection automático.
1.3 Usos
| Área | Bibliotecas |
|---|---|
| Web | Django, Flask, FastAPI |
| Data Analysis | pandas, numpy |
| Machine Learning | scikit-learn, TensorFlow, PyTorch |
| Visualization | matplotlib, seaborn, plotly |
| Web Scraping | BeautifulSoup, Scrapy |
| Automation | Selenium, PyAutoGUI |
| DevOps | Ansible, Fabric |
| Game Dev | Pygame |
| Scientific | SciPy, SymPy |
| GUI | tkinter, PyQt |
1.4 Carreira
Funções procuradas: - Backend Developer (Django, Flask, FastAPI). - Data Engineer / Analyst / Scientist. - ML Engineer. - DevOps Engineer. - Automation Engineer.
Salários médios em Portugal (2026): - Junior: 18-25 k€/ano. - Mid: 30-45 k€/ano. - Sénior: 50-80 k€/ano. - Em remote international: 2-3× isto.
2. Setup
2.1 Instalação
Windows / Mac / Linux: download python.org.
Garantir Python 3.10+ (3.11 ou 3.12 recomendado).
Verificar:
python --version
# Python 3.12.0
Em alguns sistemas, usar python3 se python é versão 2.
2.2 Editor / IDE
VSCode (recomendado gratuito): - Extensão "Python" da Microsoft. - Formatação, linting, debugging incluídos.
PyCharm (gratuito Community, pago Pro): - IDE poderosa. - Mais features que VSCode.
Jupyter Notebook:
- Para data science.
- Mistura código + texto + visualizações.
- pip install jupyter.
2.3 REPL
$ python
Python 3.12.0
>>> print("Olá")
Olá
>>> 2 + 3
5
>>> exit()
REPL = Read-Eval-Print Loop. Útil para experimentar.
Avançado: IPython (pip install ipython).
2.4 Primeiro programa
hello.py:
nome = input("Como te chamas? ")
print(f"Olá, {nome}!")
Correr:
python hello.py
3. Sintaxe básica
3.1 Variáveis
nome = "Joana" # str
idade = 25 # int
altura = 1.65 # float
estudante = True # bool
nada = None # NoneType
# Dynamic typing
x = 5
x = "agora string" # OK
# Type hints (recomendado em código profissional)
nome: str = "Joana"
idade: int = 25
Convenções (PEP 8):
- Variáveis: snake_case.
- Constantes: MAIUSCULAS_SNAKE_CASE.
- Classes: PascalCase.
3.2 Tipos básicos
int: inteiros (qualquer tamanho).
x = 42
y = 1_000_000 # readability
z = 10 ** 100 # números grandes OK
float: decimais (double precision).
pi = 3.14
e = 2.71828
str: strings (Unicode).
a = "hello"
b = 'hello'
c = """
multi-line
string
"""
bool: True / False.
ativo = True
desligado = False
None: ausência de valor.
resultado = None
3.3 Operadores
Aritméticos:
5 + 3 # 8
5 - 3 # 2
5 * 3 # 15
5 / 3 # 1.6666... (sempre float)
5 // 3 # 1 (divisão inteira)
5 % 3 # 2 (resto)
5 ** 3 # 125 (potência)
Comparação:
5 == 3 # False
5 != 3 # True
5 < 3 # False
5 > 3 # True
5 <= 5 # True
5 >= 5 # True
Lógicos:
True and False # False
True or False # True
not True # False
# Short-circuit evaluation
x = 0
x and 1/x # 0 (não dá ZeroDivisionError)
Atribuição composta:
x = 5
x += 1 # x = 6
x -= 2 # x = 4
x *= 3 # x = 12
x //= 4 # x = 3
3.4 Strings
Criar:
nome = "Python"
descricao = 'linguagem fixe'
longo = """
linha 1
linha 2
"""
Métodos comuns:
"olá".upper() # 'OLÁ'
"OLÁ".lower() # 'olá'
"hello".capitalize() # 'Hello'
"hello world".title() # 'Hello World'
" espaço ".strip() # 'espaço'
"abc".replace("b","X") # 'aXc'
"a,b,c".split(",") # ['a', 'b', 'c']
",".join(['a','b']) # 'a,b'
"abc".startswith("a") # True
"abc".endswith("c") # True
"abc".find("b") # 1
"abc".count("a") # 1
Indexing e slicing:
s = "Python"
s[0] # 'P'
s[-1] # 'n' (último)
s[0:3] # 'Pyt'
s[3:] # 'hon'
s[:3] # 'Pyt'
s[::2] # 'Pto' (step)
s[::-1] # 'nohtyP' (reverso)
F-strings (recomendado, Python 3.6+):
nome = "Ana"
idade = 25
print(f"{nome} tem {idade} anos")
# Ana tem 25 anos
# Com formatação
preco = 19.99
print(f"Preço: {preco:.2f}€") # Preço: 19.99€
# Com expressões
print(f"{nome.upper()} tem {idade * 2} anos no espelho")
3.5 Input/Output
# Output
print("Olá")
print("a", "b", "c") # 'a b c'
print("a", "b", sep="-") # 'a-b'
print("a", end="") # sem newline
# Input (sempre string)
nome = input("Nome: ")
idade = int(input("Idade: ")) # converter para int
altura = float(input("Altura: "))
4. Estruturas de controlo
4.1 If / Elif / Else
idade = 18
if idade < 13:
print("criança")
elif idade < 18:
print("adolescente")
elif idade < 65:
print("adulto")
else:
print("sénior")
Importante: indentação obrigatória (4 espaços standard).
Operador ternário:
status = "adulto" if idade >= 18 else "menor"
Match-case (Python 3.10+):
match comando:
case "start":
iniciar()
case "stop":
parar()
case _:
print("desconhecido")
4.2 For
Iterar sequência:
for fruta in ["maçã", "banana", "uva"]:
print(fruta)
Range:
for i in range(5): # 0, 1, 2, 3, 4
print(i)
for i in range(2, 10): # 2 a 9
print(i)
for i in range(0, 20, 2): # 0, 2, 4, ...
print(i)
Enumerate (índice + valor):
for i, fruta in enumerate(["a", "b", "c"]):
print(f"{i}: {fruta}")
Zip (múltiplas listas):
nomes = ["Ana", "João"]
idades = [25, 30]
for nome, idade in zip(nomes, idades):
print(f"{nome}: {idade}")
Iterar dicionário:
pessoa = {"nome": "Ana", "idade": 25}
for chave in pessoa:
print(chave)
for chave, valor in pessoa.items():
print(f"{chave}: {valor}")
4.3 While
contador = 0
while contador < 5:
print(contador)
contador += 1
Break / Continue:
while True:
resp = input("Continuar? ")
if resp == "n":
break # sai do loop
if resp == "":
continue # próxima iteração
print(resp)
Else em loops (executa se loop terminar sem break):
for i in range(10):
if i == 100:
break
else:
print("Não encontrou 100")
4.4 Try / Except
try:
numero = int(input("Número: "))
resultado = 10 / numero
print(resultado)
except ValueError:
print("Não é número válido!")
except ZeroDivisionError:
print("Não posso dividir por zero!")
except Exception as e:
print(f"Erro inesperado: {e}")
else:
print("Sem erros!") # corre se try não tiver excepção
finally:
print("Sempre corre, com ou sem erro")
Custom exceptions:
class IdadeInvalidaError(Exception):
pass
def validar_idade(idade):
if idade < 0:
raise IdadeInvalidaError("Idade não pode ser negativa")
return idade
5. Funções
5.1 Definir e chamar
def saudar(nome):
return f"Olá, {nome}!"
mensagem = saudar("Ana")
print(mensagem)
5.2 Parâmetros
Default values:
def saudar(nome, saudacao="Olá"):
return f"{saudacao}, {nome}!"
saudar("Ana") # Olá, Ana!
saudar("Ana", "Hi") # Hi, Ana!
Keyword arguments:
def criar_user(nome, idade, email):
return {"nome": nome, "idade": idade, "email": email}
# Posicional
criar_user("Ana", 25, "ana@x.pt")
# Keyword (mais claro)
criar_user(nome="Ana", idade=25, email="ana@x.pt")
# Misto (posicional primeiro)
criar_user("Ana", idade=25, email="ana@x.pt")
Args e Kwargs variáveis:
def somar_todos(*numeros):
return sum(numeros)
somar_todos(1, 2, 3, 4) # 10
def info_pessoa(**dados):
for k, v in dados.items():
print(f"{k}: {v}")
info_pessoa(nome="Ana", idade=25, cidade="Lisboa")
5.3 Type hints
def soma(a: int, b: int) -> int:
return a + b
def saudar(nome: str, formal: bool = False) -> str:
if formal:
return f"Bom dia, Senhor(a) {nome}"
return f"Olá, {nome}"
# Em listas/dicts
from typing import List, Dict, Optional
def processar(items: List[int]) -> Dict[str, int]:
return {"total": sum(items), "count": len(items)}
def buscar(id: int) -> Optional[str]:
# retorna str ou None
pass
5.4 Lambda
quadrado = lambda x: x ** 2
quadrado(5) # 25
# Com map / filter / sorted
numeros = [3, 1, 4, 1, 5, 9, 2, 6]
quadrados = list(map(lambda x: x**2, numeros))
pares = list(filter(lambda x: x % 2 == 0, numeros))
ordenados = sorted(numeros, reverse=True)
# Com key personalizado
palavras = ["maçã", "banana", "uva"]
por_tamanho = sorted(palavras, key=lambda p: len(p))
# ['uva', 'maçã', 'banana']
5.5 Docstrings
def calcular_imc(peso: float, altura: float) -> float:
"""
Calcula o Índice de Massa Corporal.
Args:
peso: peso em quilogramas.
altura: altura em metros.
Returns:
IMC como float.
Raises:
ValueError: se altura for zero.
Examples:
>>> calcular_imc(70, 1.75)
22.857142857142858
"""
if altura == 0:
raise ValueError("Altura não pode ser zero")
return peso / (altura ** 2)
Aceder: help(calcular_imc).
5.6 Decorators
import time
def timing(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} demorou {end-start:.4f}s")
return result
return wrapper
@timing
def calcular_pesado(n):
return sum(range(n))
calcular_pesado(1000000)
# calcular_pesado demorou 0.0234s
6. Estruturas de dados
6.1 Listas
# Criar
frutas = ["maçã", "banana", "uva"]
vazia = []
nums = list(range(5)) # [0, 1, 2, 3, 4]
# Acesso e slicing (igual strings)
frutas[0] # "maçã"
frutas[-1] # "uva"
frutas[1:] # ["banana", "uva"]
# Modificar
frutas[0] = "kiwi"
frutas.append("laranja")
frutas.insert(0, "manga")
frutas.extend(["pêra", "ameixa"])
# Remover
frutas.remove("kiwi") # primeiro com este valor
del frutas[0] # por índice
ultima = frutas.pop() # remove e retorna último
primeira = frutas.pop(0) # remove e retorna primeiro
# Ordenar
frutas.sort()
frutas.sort(reverse=True)
frutas.sort(key=len) # por comprimento
# Reverso
frutas.reverse()
# Procurar
frutas.index("banana") # índice
frutas.count("uva") # quantas vezes
"banana" in frutas # True/False
# Comprimento
len(frutas)
# Cópia
copia = frutas.copy()
# ou: copia = frutas[:]
# ou: copia = list(frutas)
6.2 List comprehensions
# Em vez de:
quadrados = []
for x in range(10):
quadrados.append(x ** 2)
# Forma idiomática:
quadrados = [x ** 2 for x in range(10)]
# Com condição
pares = [x for x in range(20) if x % 2 == 0]
# Aninhada
matriz = [[i*j for j in range(5)] for i in range(5)]
# Transformar dict em list
nomes = [pessoa["nome"] for pessoa in lista_pessoas]
# Achatar lista de listas
flatten = [item for sublist in lista_listas for item in sublist]
Boa prática: usar quando é simples. Para lógica complexa, usar for tradicional.
6.3 Tuplos
# Imutáveis (não podem ser alterados após criação)
ponto = (3, 4)
cores = ("vermelho", "verde", "azul")
um_elemento = (42,) # vírgula obrigatória para 1 elem
# Acesso
ponto[0] # 3
# Unpacking
x, y = ponto
r, g, b = cores
# Multiplos returns
def coordenadas():
return 10, 20
x, y = coordenadas()
Usar tuplos quando: - Valor não muda (imutabilidade). - Heterogéneo (vários tipos). - Como chave de dict.
Usar listas quando: - Vai modificar. - Homogéneo (mesmo tipo). - Sequência de coisas similares.
6.4 Dicionários
# Criar
pessoa = {
"nome": "Ana",
"idade": 25,
"cidade": "Lisboa"
}
vazio = {}
de_zip = dict(zip(["a", "b"], [1, 2]))
# Acesso
pessoa["nome"] # "Ana"
pessoa.get("email") # None (não levanta erro)
pessoa.get("email", "N/A") # default
# Modificar / adicionar
pessoa["idade"] = 26
pessoa["email"] = "ana@x.pt"
# Remover
del pessoa["cidade"]
removido = pessoa.pop("idade")
# Iterar
for chave in pessoa: # apenas chaves
print(chave)
for valor in pessoa.values():
print(valor)
for chave, valor in pessoa.items():
print(f"{chave}: {valor}")
# Verificar
"nome" in pessoa # True
# Tamanho
len(pessoa)
# Dict comprehension
quadrados = {x: x**2 for x in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
6.5 Sets
# Conjuntos (sem duplicados, sem ordem)
fruits = {"apple", "banana", "orange"}
duplicados = {1, 2, 2, 3, 3, 3} # {1, 2, 3}
vazio = set() # NÃO {} (é dict)
# Operações
fruits.add("kiwi")
fruits.remove("banana")
fruits.discard("nada") # não erro se não existe
# Operações de conjuntos
a = {1, 2, 3}
b = {2, 3, 4}
a | b # união: {1, 2, 3, 4}
a & b # intersecção: {2, 3}
a - b # diferença: {1}
a ^ b # diferença simétrica: {1, 4}
# Verificar
2 in a # True
Usar sets quando:
- Procura rápida (in é O(1)).
- Eliminar duplicados.
- Operações matemáticas de conjuntos.
7. OOP — Programação Orientada a Objectos
7.1 Classes
class Pessoa:
# Atributo de classe (partilhado)
especie = "Homo sapiens"
# Construtor
def __init__(self, nome, idade):
self.nome = nome # atributos de instância
self.idade = idade
# Método
def saudar(self):
return f"Olá, sou {self.nome}"
# Method magic / dunder
def __repr__(self):
return f"Pessoa(nome={self.nome!r}, idade={self.idade})"
def __str__(self):
return f"{self.nome} ({self.idade} anos)"
def __eq__(self, other):
return self.nome == other.nome and self.idade == other.idade
# Usar
ana = Pessoa("Ana", 25)
joao = Pessoa("João", 30)
print(ana.saudar()) # Olá, sou Ana
print(ana) # Ana (25 anos)
print(repr(ana)) # Pessoa(nome='Ana', idade=25)
7.2 Herança
class Animal:
def __init__(self, nome):
self.nome = nome
def fala(self):
raise NotImplementedError("Subclasses devem implementar")
def info(self):
return f"Sou um animal chamado {self.nome}"
class Cao(Animal):
def fala(self):
return "Au au!"
def trazer_bola(self):
return f"{self.nome} traz a bola"
class Gato(Animal):
def fala(self):
return "Miau"
class Cachorro(Cao): # Herança múltipla a nível
def __init__(self, nome, idade):
super().__init__(nome)
self.idade = idade
def fala(self):
return "Auf!" # cachorro fala diferente
def info(self):
return super().info() + f" e tenho {self.idade} meses"
rex = Cao("Rex")
print(rex.fala()) # Au au!
print(rex.info()) # Sou um animal chamado Rex
felix = Gato("Felix")
print(felix.fala()) # Miau
zé = Cachorro("Zé", 3)
print(zé.fala()) # Auf!
print(zé.info()) # Sou um animal chamado Zé e tenho 3 meses
7.3 Encapsulamento
class ContaBancaria:
def __init__(self, titular, saldo_inicial=0):
self.titular = titular
self._saldo = saldo_inicial # _ = convenção "privado"
self.__pin = "1234" # __ = name mangling
def depositar(self, valor):
if valor <= 0:
raise ValueError("Valor deve ser positivo")
self._saldo += valor
def levantar(self, valor):
if valor <= 0:
raise ValueError("Valor deve ser positivo")
if valor > self._saldo:
raise ValueError("Saldo insuficiente")
self._saldo -= valor
@property
def saldo(self):
return self._saldo
@saldo.setter
def saldo(self, valor):
raise AttributeError("Não pode modificar saldo directamente")
conta = ContaBancaria("Ana", 1000)
print(conta.saldo) # 1000
conta.depositar(500)
print(conta.saldo) # 1500
conta.levantar(200)
print(conta.saldo) # 1300
# conta.saldo = 1000000 # AttributeError!
7.4 Polimorfismo
def fazer_falar(animal):
print(animal.fala())
animais = [Cao("Rex"), Gato("Felix"), Cao("Bobi")]
for animal in animais:
fazer_falar(animal)
# Au au!
# Miau
# Au au!
Mesma interface, comportamentos diferentes.
7.5 Classmethod e Staticmethod
class Pessoa:
contador = 0
def __init__(self, nome):
self.nome = nome
Pessoa.contador += 1
@classmethod
def total(cls):
return cls.contador
@staticmethod
def é_maior_idade(idade):
return idade >= 18
# Usar
ana = Pessoa("Ana")
joao = Pessoa("João")
print(Pessoa.total()) # 2
print(Pessoa.é_maior_idade(20)) # True
7.6 Dataclasses (Python 3.7+)
from dataclasses import dataclass
@dataclass
class Ponto:
x: float
y: float
def distancia(self, outro):
return ((self.x - outro.x)**2 + (self.y - outro.y)**2)**0.5
p1 = Ponto(0, 0)
p2 = Ponto(3, 4)
print(p1) # Ponto(x=0, y=0)
print(p1.distancia(p2)) # 5.0
print(p1 == Ponto(0,0)) # True (equality automático)
Reduz boilerplate.
8. Bibliotecas essenciais
8.1 Standard library
math:
import math
math.sqrt(16) # 4.0
math.pi # 3.14159...
math.ceil(4.2) # 5
math.floor(4.8) # 4
math.factorial(5) # 120
random:
import random
random.random() # 0.0 a 1.0
random.randint(1, 10) # 1 a 10 inclusive
random.choice(["a","b"]) # escolhe um
random.shuffle(lista) # baralha (in-place)
random.sample(lista, 3) # 3 únicos
datetime:
from datetime import datetime, timedelta, date
agora = datetime.now()
hoje = date.today()
amanha = agora + timedelta(days=1)
formato = agora.strftime("%Y-%m-%d %H:%M")
parsed = datetime.strptime("2026-05-22", "%Y-%m-%d")
os / pathlib:
import os
from pathlib import Path
# Moderno: pathlib
p = Path("dados/file.txt")
p.exists()
p.read_text()
p.write_text("conteúdo")
p.suffix # .txt
p.parent # Path("dados")
for f in Path(".").glob("*.py"):
print(f)
json:
import json
# Dict → JSON string
data = {"nome": "Ana", "idade": 25}
json_str = json.dumps(data, indent=2)
# JSON string → dict
data = json.loads('{"x": 1, "y": 2}')
# Ficheiro
with open("data.json") as f:
data = json.load(f)
with open("out.json", "w") as f:
json.dump(data, f, indent=2)
collections:
from collections import Counter, defaultdict, namedtuple
# Counter
votes = ["a", "b", "a", "c", "a", "b"]
Counter(votes) # Counter({'a': 3, 'b': 2, 'c': 1})
# defaultdict
dd = defaultdict(list)
dd["chave"].append(1) # não erro se não existe
# namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
p.x # 3
8.2 Ficheiros
# Ler
with open("arquivo.txt", "r", encoding="utf-8") as f:
conteudo = f.read() # tudo
linhas = f.readlines() # lista de linhas
# ou iterar
for linha in f:
print(linha.strip())
# Escrever
with open("output.txt", "w", encoding="utf-8") as f:
f.write("Olá\n")
f.writelines(["linha 1\n", "linha 2\n"])
# Anexar
with open("log.txt", "a") as f:
f.write("nova entrada\n")
# CSV
import csv
with open("dados.csv") as f:
reader = csv.DictReader(f)
for linha in reader:
print(linha["nome"], linha["idade"])
with open("saida.csv", "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=["nome", "idade"])
writer.writeheader()
writer.writerow({"nome": "Ana", "idade": 25})
8.3 pip + venv
# Criar venv
python -m venv venv
# Activar
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
# Instalar pacotes
pip install requests pandas numpy
# Listar
pip list
# Guardar
pip freeze > requirements.txt
# Instalar de requirements
pip install -r requirements.txt
# Desactivar
deactivate
Sempre usar venv por projecto (evita conflitos).
8.4 requests (HTTP)
import requests
# GET
r = requests.get("https://api.github.com/users/joaosilva")
data = r.json()
print(data["name"])
# POST
r = requests.post(
"https://api.example.com/users",
json={"nome": "Ana", "email": "ana@x.pt"},
headers={"Authorization": "Bearer token"}
)
# Verificar status
if r.status_code == 200:
print("OK")
else:
print(f"Erro: {r.status_code}")
8.5 pandas (data analysis)
import pandas as pd
# Ler CSV
df = pd.read_csv("dados.csv")
# Explorar
df.head() # primeiras 5 linhas
df.info() # info estrutural
df.describe() # estatísticas
df.shape # (linhas, colunas)
df.columns # nome colunas
# Seleccionar
df["nome"] # 1 coluna
df[["nome", "idade"]] # várias
# Filtrar
df[df["idade"] > 25]
df[(df["idade"] > 25) & (df["cidade"] == "Lisboa")]
# Agrupar
df.groupby("cidade")["idade"].mean()
# Modificar
df["idade_dobro"] = df["idade"] * 2
df.dropna() # remover NaN
df.fillna(0) # substituir NaN
# Guardar
df.to_csv("output.csv", index=False)
df.to_excel("output.xlsx")
8.6 matplotlib (visualização)
import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5]
y = [2, 4, 6, 8, 10]
# Linha
plt.plot(x, y)
plt.title("Meu Gráfico")
plt.xlabel("X")
plt.ylabel("Y")
plt.show()
# Bar chart
categorias = ["A", "B", "C"]
valores = [10, 20, 15]
plt.bar(categorias, valores)
plt.show()
# Scatter
plt.scatter([1,2,3], [4,5,6])
plt.show()
# Guardar
plt.savefig("grafico.png")
9. Boas práticas (PEP 8)
9.1 Estilo
- Indentação: 4 espaços (não tabs).
- Linhas: máximo 79 caracteres (ou 99 com Black).
- Imports: no topo do ficheiro, ordenados (standard → third-party → local).
- Blank lines: 2 entre funções top-level, 1 entre métodos.
# Bom
def calcular(x, y):
"""Soma dois números."""
return x + y
def subtrair(x, y):
"""Subtrai dois números."""
return x - y
9.2 Naming conventions
# Variáveis e funções: snake_case
nome_completo = "Ana Silva"
def calcular_imc(peso, altura):
pass
# Classes: PascalCase
class ContaBancaria:
pass
# Constantes: UPPER_SNAKE_CASE
PI = 3.14159
MAX_USERS = 100
# Privado: prefixo _
self._saldo = 0
# Muito privado (name mangling): __
self.__pin = "1234"
9.3 Docstrings
def funcao(arg1: int, arg2: str) -> bool:
"""
Breve descrição em uma linha.
Descrição mais detalhada se necessário, podendo
estender por várias linhas.
Args:
arg1: descrição do primeiro arg.
arg2: descrição do segundo arg.
Returns:
Descrição do retorno.
Raises:
ValueError: se algo correr mal.
"""
pass
9.4 Ferramentas
Black — formatador automático:
pip install black
black meu_codigo.py
flake8 — linter:
pip install flake8
flake8 meu_codigo.py
mypy — type checker:
pip install mypy
mypy meu_codigo.py
pytest — testes:
pip install pytest
pytest test_arquivo.py
Usar pre-commit hooks para automatizar.
10. Projecto exemplo
10.1 Estrutura
meu-projecto/
├── venv/ (não commitar)
├── src/
│ ├── __init__.py
│ ├── main.py
│ ├── models.py
│ └── utils.py
├── tests/
│ ├── __init__.py
│ ├── test_main.py
│ └── test_utils.py
├── docs/
│ └── README.md
├── .gitignore
├── README.md
├── requirements.txt
└── pyproject.toml
10.2 Exemplo: CLI Todo App
src/main.py:
import json
from pathlib import Path
from datetime import datetime
DB_PATH = Path("todos.json")
def carregar_todos():
"""Carrega todos do ficheiro JSON."""
if DB_PATH.exists():
return json.loads(DB_PATH.read_text())
return []
def guardar_todos(todos):
"""Guarda todos no ficheiro JSON."""
DB_PATH.write_text(json.dumps(todos, indent=2, default=str))
def adicionar(texto):
"""Adiciona novo todo."""
todos = carregar_todos()
todos.append({
"id": len(todos) + 1,
"texto": texto,
"feito": False,
"criado": datetime.now().isoformat()
})
guardar_todos(todos)
print(f"✓ Adicionado: {texto}")
def listar():
"""Lista todos os todos."""
todos = carregar_todos()
if not todos:
print("Sem todos.")
return
for t in todos:
check = "✓" if t["feito"] else " "
print(f"[{check}] {t['id']}. {t['texto']}")
def marcar_feito(id):
"""Marca todo como feito."""
todos = carregar_todos()
for t in todos:
if t["id"] == id:
t["feito"] = True
guardar_todos(todos)
print(f"✓ {t['texto']} marcado como feito")
return
print(f"Todo {id} não encontrado")
def main():
"""Loop principal."""
while True:
print("\n--- TODO ---")
print("1. Adicionar")
print("2. Listar")
print("3. Marcar feito")
print("0. Sair")
escolha = input("Opção: ")
if escolha == "1":
texto = input("O quê? ")
adicionar(texto)
elif escolha == "2":
listar()
elif escolha == "3":
id = int(input("ID: "))
marcar_feito(id)
elif escolha == "0":
break
if __name__ == "__main__":
main()
Correr: python src/main.py.
10.3 Testes
tests/test_main.py:
import pytest
from src.main import carregar_todos, guardar_todos
def test_carregar_vazio(tmp_path, monkeypatch):
monkeypatch.setattr("src.main.DB_PATH", tmp_path / "todos.json")
assert carregar_todos() == []
def test_guardar_e_carregar(tmp_path, monkeypatch):
monkeypatch.setattr("src.main.DB_PATH", tmp_path / "todos.json")
todos = [{"id": 1, "texto": "teste", "feito": False}]
guardar_todos(todos)
assert carregar_todos() == todos
Correr: pytest.
Apêndice A · PEP 20 — Zen of Python
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
Correr: import this.
Apêndice B · Recursos
Livros: - "Automate the Boring Stuff with Python" — Al Sweigart (gratuito online). - "Python Crash Course" — Eric Matthes. - "Fluent Python" — Luciano Ramalho (avançado). - "Effective Python" — Brett Slatkin.
Cursos online: - freeCodeCamp: Python for Everybody (gratuito). - Coursera: Python for Everybody (Michigan). - Real Python: realpython.com (artigos + cursos).
Prática: - HackerRank: hackerrank.com/domains/python. - LeetCode: leetcode.com (interview prep). - Codewars: codewars.com. - Exercism: exercism.org (mentoria).
Documentação: - Oficial: docs.python.org. - Python Package Index: pypi.org.
Apêndice C · Glossário
- PEP: Python Enhancement Proposal (especificações).
- REPL: Read-Eval-Print Loop.
- venv: virtual environment.
- pip: package installer.
- Pythonic: código no estilo idiomático Python.
- Duck typing: "if it walks like a duck...".
- GIL: Global Interpreter Lock.
- Decorators: funções que modificam funções.
- Generator: função com yield (lazy iteration).
- Iterator / Iterable: protocolo de iteração.
- Comprehension: list/dict/set comprehension.
- Lambda: função anónima.
- Type hints: anotações de tipo (PEP 484).
- Dunder: double underscore methods (
__init__,__str__).