1
resposta

[Sugestão] Hora da prática: criando classes, construtores e métodos

Não tratei os possiveis erros de entrada do usuário para ser mais direto. Os @classmethod que definei fazem sentido, esses metodos devem ficar detro da class ou devem ficar fora? O que mais deveria ser melhorado ? Como seria o jeito "pythonyco" de fazer esse código ?

class ContaBancaria:
    lista_contas = []
    def __init__(self, titular, saldo):
        self.titular = titular
        self.saldo = saldo
        self.ativo = False
        ContaBancaria.lista_contas.append(self)


    def __str__(self):
        return f'{self.titular} | {self.saldo}'
    
    @classmethod
    def listar_contas(cls):
        
        print(f'{'Nome titular'.ljust(25)} | {'Saldo em conta'.ljust(25)} | Status da Conta')
        for conta in cls.lista_contas:
            print(f'{conta.titular.ljust(25)} | {conta.saldo.ljust(25)} | {conta.ativo}')

        input('Digite uma tecla para voltar ao menu principal')
        menu()

    @classmethod
    def cadastrar_conta(cls):
        nome = input('Digite o nome do titular: ')
        saldo = input('Digite o valor do deposito: ')
        cls(nome, saldo)
        input('Digite uma tecla para voltar ao menu principal')
        menu()
    
    @classmethod
    def ativar_conta(cls):
        nome_procurado = input('Digite o nome do titular da conta')
        for conta in cls.lista_contas:
            if conta.titular == nome_procurado:
                conta.ativo = not conta.ativo
                print(f'Conta ativada' if conta.ativo else 'Conta Desativada')
        input('Digite uma tecla para voltar ao menu principal')
        menu()


def menu():
    print('1.Cadastrar conta')
    print('2.Listar contas')
    print('3.Ativar conta')
    print('4.Sair')
    selecao = input('Digite uma opção: ')
    opcoes(selecao)


def opcoes(selecao):
    if selecao == '1':
        ContaBancaria.cadastrar_conta()
    elif selecao == '2':
        ContaBancaria.listar_contas()
    elif selecao == '3':
        ContaBancaria.ativar_conta()
    
    elif selecao == '4':
        print('Saindo do programa')


def main():
    menu()
    
if __name__ == '__main__':
    main()
    
      
1 resposta

Oi, William!

Que legal ver seu interesse em aprofundar na Orientação a Objetos com Python! Você trouxe uma implementação funcional que mostra que você compreendeu a lógica de manipulação de listas e classes.

Vou analisar suas dúvidas e sugerir algumas melhorias para deixar seu código mais alinhado às boas práticas da linguagem.

1. Métodos dentro ou fora da classe?

Os métodos que você criou (listar_contas, cadastrar_conta, ativar_conta) como @classmethod funcionam, mas na arquitetura de software, costumamos separar a lógica do modelo (a classe ContaBancaria) da lógica de interface/controle (o menu e os inputs).

  • Dentro da classe: Deixe apenas o que diz respeito ao objeto ou ao conjunto de objetos (ex: uma lista de contas).
  • Fora da classe: Coloque as funções que interagem com o usuário (input e print). Isso permite que você use a classe ContaBancaria em um site, em um aplicativo ou em um terminal sem precisar mudar o código dela.

2. A Abordagem "Pythonica"

Em Python, evitamos usar getters e setters como em Java ou C#, a menos que haja necessidade de validação. O uso de @property é a forma ideal de proteger atributos (usando o prefixo _) e ainda assim permitir o acesso direto.

Sugestão de Refatoração

Olha uma versão que separa as responsabilidades e utiliza recursos mais modernos do Python:

class ContaBancaria:
    contas = []

    def __init__(self, titular, saldo):
        self._titular = titular.title() # Tratamento simples de string
        self._saldo = float(saldo)      # Garante que o saldo seja numérico
        self._ativo = False
        ContaBancaria.contas.append(self)

    def __str__(self):
        status = "Ativa" if self._ativo else "Inativa"
        return f"{self._titular.ljust(20)} | R$ {str(self._saldo).ljust(10)} | {status}"

    @property
    def titular(self):
        return self._titular

    @property
    def ativo(self):
        return self._ativo

    def alternar_estado(self):
        self._ativo = not self._ativo

Por que essas mudanças?

  • Encapsulamento: Usamos _titular e _saldo para indicar que são atributos internos. O acesso é feito via @property.
  • Separação de interface: Note que não há input() dentro da classe. A interação com o usuário deve acontecer em uma função principal.
  • Formatação: O método __str__ agora cuida de como a conta se apresenta, facilitando a listagem.

O que pode ser melhorado no Fluxo

Um detalhe importante no seu código original é a recursividade (uma função chamando a outra infinitamente: menu -> cadastrar -> menu). Isso pode causar um erro de estouro de pilha se o programa rodar por muito tempo.

O ideal é usar um laço de repetição while:

def main():
    while True:
        print("\n1. Cadastrar conta\n2. Listar contas\n3. Alternar Status\n4. Sair")
        opcao = input("Escolha: ")

        if opcao == '1':
            nome = input("Titular: ")
            valor = input("Saldo inicial: ")
            ContaBancaria(nome, valor)
        elif opcao == '2':
            for conta in ContaBancaria.contas:
                print(conta)
        elif opcao == '3':
            nome_busca = input("Nome do titular: ")
            for conta in ContaBancaria.contas:
                if conta.titular == nome_busca:
                    conta.alternar_estado()
        elif opcao == '4':
            break

Essa estrutura é mais limpa e evita que o programa se perca em chamadas de funções desnecessárias.

Alura Conte com o apoio da comunidade Alura na sua jornada. Abraços e bons estudos!