Ficha 01 · Classes e encapsulamento
- Modelar com classes
- Aplicar encapsulamento
- Usar properties
- Definir métodos especiais
Parte I · Classes
Exercício 1 · Classe Livro (15 pts)
Cria uma classe Livro com atributos titulo, autor, ano, paginas. Adiciona método descrever() que devolve string formatada. Testa criando 2 livros e imprimindo.
class Livro:
def __init__(self, titulo, autor, ano, paginas):
self.titulo = titulo
self.autor = autor
self.ano = ano
self.paginas = paginas
def descrever(self):
return f"'{self.titulo}' de {self.autor} ({self.ano}, {self.paginas}p)"
l1 = Livro("Os Lusíadas", "Camões", 1572, 600)
l2 = Livro("Saramago", "Ensaio sobre a cegueira", 1995, 350)
print(l1.descrever())
print(l2.descrever())
Exercício 2 · Método repr (10 pts)
Adiciona __repr__ e __str__ à classe Livro. Qual a diferença entre os dois?
class Livro:
def __init__(self, titulo, autor, ano):
self.titulo = titulo
self.autor = autor
self.ano = ano
def __repr__(self):
return f"Livro({self.titulo!r}, {self.autor!r}, {self.ano})"
def __str__(self):
return f"'{self.titulo}' — {self.autor}"
l = Livro("Saramago", "Ensaio sobre a cegueira", 1995)
print(repr(l)) # Livro('Saramago', ...) — para programadores
print(str(l)) # 'Saramago' — Saramago — para utilizadores
print(l) # usa __str__
Diferença:
- __repr__ — representação inequívoca para programadores; idealmente código que recria o objecto.
- __str__ — representação legível para utilizadores. Se não definida, usa __repr__.
Parte II · Encapsulamento
Exercício 3 · Conta bancária (20 pts)
Cria classe ContaBancaria com:
- Atributo "privado" _saldo.
- Métodos depositar(valor), levantar(valor), saldo().
- Levantar deve falhar com excepção se saldo insuficiente ou valor negativo.
class ContaBancaria:
def __init__(self, titular, saldo_inicial=0):
self.titular = titular
self._saldo = saldo_inicial
def depositar(self, valor):
if valor <= 0:
raise ValueError("Valor tem de ser positivo")
self._saldo += valor
def levantar(self, valor):
if valor <= 0:
raise ValueError("Valor tem de ser positivo")
if valor > self._saldo:
raise ValueError("Saldo insuficiente")
self._saldo -= valor
def saldo(self):
return self._saldo
# Teste
c = ContaBancaria("Ana", 100)
c.depositar(50)
print(c.saldo()) # 150
c.levantar(30)
print(c.saldo()) # 120
# c.levantar(1000) # ValueError
Exercício 4 · Properties (15 pts)
Adiciona à ContaBancaria uma property saldo (em vez do método) e validar que o titular tem mínimo 3 caracteres no construtor.
class ContaBancaria:
def __init__(self, titular, saldo_inicial=0):
if len(titular) < 3:
raise ValueError("Titular tem de ter pelo menos 3 caracteres")
self.titular = titular
self._saldo = saldo_inicial
@property
def saldo(self):
return self._saldo
# Sem setter — saldo só muda por depositar/levantar
def depositar(self, valor):
if valor <= 0:
raise ValueError("Valor positivo")
self._saldo += valor
def levantar(self, valor):
if valor <= 0 or valor > self._saldo:
raise ValueError("Inválido")
self._saldo -= valor
c = ContaBancaria("Ana", 100)
print(c.saldo) # 150 — sem parêntesis
# c.saldo = 9999 # AttributeError — não há setter
Parte III · Métodos especiais
Exercício 5 · Classe Vetor (20 pts)
Cria classe Vetor com x, y. Implementa:
- __init__
- __repr__
- __add__ — soma de vectores.
- __eq__ — comparação.
- __abs__ — magnitude.
import math
class Vetor:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vetor({self.x}, {self.y})"
def __add__(self, outro):
return Vetor(self.x + outro.x, self.y + outro.y)
def __eq__(self, outro):
return isinstance(outro, Vetor) and self.x == outro.x and self.y == outro.y
def __abs__(self):
return math.sqrt(self.x**2 + self.y**2)
v1 = Vetor(3, 4)
v2 = Vetor(1, 2)
print(v1 + v2) # Vetor(4, 6)
print(abs(v1)) # 5.0
print(v1 == Vetor(3, 4)) # True
Parte IV · Cenário
Exercício 6 · Sistema de aluguer (20 pts)
Modela um sistema de aluguer de bicicletas com:
- Classe
Bicicleta:id,modelo,disponivel(default True). - Classe
Aluguer:bicicleta,cliente,inicio,fim(inicialmente None). - Métodos:
Bicicleta.alugar(cliente)— cria aluguer, marca indisponível.Aluguer.terminar()— define fim, marca bicicleta disponível.
from datetime import datetime
class Bicicleta:
def __init__(self, id, modelo):
self.id = id
self.modelo = modelo
self.disponivel = True
def alugar(self, cliente):
if not self.disponivel:
raise ValueError(f"Bicicleta {self.id} não disponível")
self.disponivel = False
return Aluguer(self, cliente, datetime.now())
def __repr__(self):
status = "✓" if self.disponivel else "✗"
return f"Bicicleta({self.id}, {self.modelo}, {status})"
class Aluguer:
def __init__(self, bicicleta, cliente, inicio):
self.bicicleta = bicicleta
self.cliente = cliente
self.inicio = inicio
self.fim = None
def terminar(self):
if self.fim is not None:
raise ValueError("Aluguer já terminado")
self.fim = datetime.now()
self.bicicleta.disponivel = True
def duracao(self):
if self.fim is None:
return datetime.now() - self.inicio
return self.fim - self.inicio
def __repr__(self):
estado = "em curso" if self.fim is None else "terminado"
return f"Aluguer({self.cliente}, {self.bicicleta.id}, {estado})"
# Teste
b1 = Bicicleta(1, "BTT")
a = b1.alugar("Ana")
print(b1) # disponível: ✗
print(a) # em curso
a.terminar()
print(b1) # disponível: ✓