Desafio Python
Conta bancária com saldo e histórico
Enunciado
Implemente uma classe ContaBancaria com as properties saldo (somente leitura, calculado a partir do histórico de transações) e historico (somente leitura, retorna uma cópia da lista de transações). A classe deve ter métodos depositar(valor) e sacar(valor). Depósitos e saques devem ser registrados no histórico como tuplas (tipo, valor), onde tipo é 'deposito' ou 'saque'. Saques não podem exceder o saldo; caso contrário, lance ValueError('Saldo insuficiente'). Depósitos e saques devem ser maiores que zero; caso contrário, lance ValueError('Valor deve ser positivo').
Requisitos
- A property
saldodeve ser somente leitura e calcular o saldo atual somando depósitos e subtraindo saques do histórico. - A property
historicodeve ser somente leitura e retornar uma cópia da lista de transações. - Os métodos
depositaresacardevem validar o valor (maior que zero) e registrar a transação no histórico. - O saque não pode deixar o saldo negativo; caso contrário, lance
ValueError('Saldo insuficiente').
Código inicial
class ContaBancaria:
def __init__(self):
self._transacoes = []
@property
def saldo(self):
pass
@property
def historico(self):
pass
def depositar(self, valor):
pass
def sacar(self, valor):
pass
Saída esperada
Saldo inicial: 0
Saldo após depósito: 100
Histórico: [('deposito', 100)]
Saldo após saque: 70
Histórico: [('deposito', 100), ('saque', 30)]
Erro: Saldo insuficiente
Erro: Valor deve ser positivo
Ver dica
No saldo, percorra self._transacoes e some os valores de depósitos, subtraia os de saques. No historico, retorne uma cópia com list(self._transacoes) para evitar modificações externas.
Mostrar solução
class ContaBancaria:
def __init__(self):
self._transacoes = [] # lista de tuplas (tipo, valor)
@property
def saldo(self):
total = 0
for tipo, valor in self._transacoes:
if tipo == 'deposito':
total += valor
else: # 'saque'
total -= valor
return total
@property
def historico(self):
return list(self._transacoes) # cópia para não expor a lista interna
def depositar(self, valor):
if valor <= 0:
raise ValueError('Valor deve ser positivo')
self._transacoes.append(('deposito', valor))
def sacar(self, valor):
if valor <= 0:
raise ValueError('Valor deve ser positivo')
if valor > self.saldo:
raise ValueError('Saldo insuficiente')
self._transacoes.append(('saque', valor))
# Testes
c = ContaBancaria()
print(f'Saldo inicial: {c.saldo}')
c.depositar(100)
print(f'Saldo após depósito: {c.saldo}')
print(f'Histórico: {c.historico}')
c.sacar(30)
print(f'Saldo após saque: {c.saldo}')
print(f'Histórico: {c.historico}')
try:
c.sacar(200)
except ValueError as e:
print(f'Erro: {e}')
try:
c.depositar(-10)
except ValueError as e:
print(f'Erro: {e}')
Passo a passo
- A classe inicia com uma lista vazia
_transacoesque armazenará tuplas(tipo, valor). - A property
saldopercorre as transações, somando depósitos e subtraindo saques, e retorna o total. Como não há setter, é somente leitura. - A property
historicoretorna uma cópia da lista (list(self._transacoes)) para que modificações externas não afetem o estado interno. - O método
depositarvalida se o valor é positivo; se não, lançaValueError. Caso contrário, adiciona a tupla('deposito', valor)ao histórico. - O método
sacartambém valida o valor positivo. Depois, verifica se o valor é maior que o saldo atual (usando a propertysaldo); se for, lançaValueError('Saldo insuficiente'). Senão, adiciona('saque', valor). - Nos testes, criamos uma conta, fazemos um depósito de 100, imprimimos saldo e histórico. Em seguida, sacamos 30 e imprimimos novamente.
- Tentamos sacar 200 (mais que o saldo) e depositar -10, capturando e imprimindo as mensagens de erro.
Por que funciona
O saldo é calculado dinamicamente a partir do histórico, então nunca fica dessincronizado. As properties fornecem acesso controlado: saldo e historico são somente leitura, impedindo alterações diretas. Os métodos depositar e sacar são os únicos pontos de entrada para modificar o estado, garantindo validações consistentes.
Erros comuns
- Retornar a lista interna diretamente: se
historicoretornarself._transacoes, o usuário pode modificar a lista e quebrar o saldo. Uselist(...)para copiar. - Não validar o valor no saque: se permitir saques negativos, o saldo aumentaria indevidamente. Sempre valide
valor > 0. - Calcular o saldo incorretamente: esquecer de subtrair saques ou somar depósitos. Revise a lógica de soma/subtração.
- Permitir saque maior que o saldo: verifique
valor > self.saldoantes de registrar a transação.
Outra forma de resolver
Poderia-se armazenar o saldo como atributo e atualizá-lo a cada operação, mas isso exigiria sincronização manual e poderia levar a inconsistências. A abordagem com histórico é mais robusta e demonstra o poder de atributos calculados.
Saída esperada
Saldo inicial: 0
Saldo após depósito: 100
Histórico: [('deposito', 100)]
Saldo após saque: 70
Histórico: [('deposito', 100), ('saque', 30)]
Erro: Saldo insuficiente
Erro: Valor deve ser positivo