Search code examples
pythonclassinitclass-attributes

OOP - init method not changing class attribute


I am trying to use the transfer_to_saving method in the CheckingAccount class. However, whenever I create a SavingAccount object, the self.has_saving = True does not change the class attribute to True. So, whenever I try to transfer funds, it prints Must create a saving account.

class CheckingAccount(Account):
    balance = 0

    def __init__(self, account_number, pin):
        super().__init__(account_number)
        self.SavingAccount = SavingAccount
        self.pin = pin

    def deposit(self, amount):
        old_bal = self.balance
        self.balance += amount
        print(f'Previous Balance: ${old_bal}\nDeposit amount: ${amount}\nNew Balance: ${self.balance}')

    def withdraw(self, pin, amount):
        if pin == self.pin:
            self.balance -= print('Insufficient funds') if amount > self.balance else amount
        else:
            print('Invalid PIN')

    def transfer_to_saving(self, amount):
        if self.SavingAccount.has_saving is False:
            print('Must create a saving account')
        elif amount > self.balance:
            print('Insufficient funds')
        else:
            self.SavingAccount.balance += amount
            self.balance -= amount


class SavingAccount(Account):
    balance = 0
    has_saving = False

    def __init__(self, account_number):
        super().__init__(account_number)
        self.CheckingAccount = CheckingAccount
        self.has_saving = True

    def deposit(self, amount):
        self.balance += amount

Am I doing this right? Shouldn't the init method be changing the class attribute?

---UPDATE---

The goal I am trying to accomplish is to find out whether the user has already created a saving account. I have additional User classes that I did not include since it would be a bit overkill. However, the goal is to prevent a user from transferring money from checking to saving if they don't have a saving account.


Solution

  • You are not actually creating an instance of SavingAccount with this line:

            self.SavingAccount = SavingAccount
    

    You are assigning the self.SavingAccount attribute to the SavingAccount class defined below.

    You need to call the SavingAccount constructor, like this:

            self.saving_account = SavingAccount(account_number)
    

    Note that the Python convention is to use lower_snake_case for attributes/variables, and UpperCamelCase for class names.

    You are doing the same thing on this line in the SavingAccount constructor:

            self.CheckingAccount = CheckingAccount
    

    I'm not sure what the goal is here, but if you want every SavingAccount to hold a reference to a CheckingAccount and vice versa, it might be cleaner to do it like this:

    class CheckingAccount(Account):
    
        def __init__(self, account_number, pin):
            super().__init__(account_number)
            self.saving_account = SavingAccount(account_number, self)
            self.pin = pin
    
    class SavingAccount(Account):
        def __init__(self, account_number, checking_account):
            super().__init__(account_number)
            self.checking_account = checking_account
    

    With this, whenever you create a CheckingAccount you will get a corresponding SavingAccount and they will each hold a reference to each other. I think it's still a bit weird conceptually, since the account numbers would be the same, so it might be better to just create them separately like this:

    class CheckingAccount(Account):
        def __init__(self, account_number, pin):
            super().__init__(account_number)
            self.saving_account = None  # to be assigned later
            self.pin = pin
    
    class SavingAccount(Account):
        def __init__(self, account_number):
            super().__init__(account_number)
            self.checking_account = None  # to be assigned later
    
    
    checking_account_number = 123
    checking = CheckingAccount(checking_account_number)
    saving_account_number = 456
    saving = SavingAccount(saving_account_number)
    checking.saving_account = saving
    saving.checking_account = checking
    

    Finally, the has_saving attribute of SavingAccount is not necessary at all. A cleaner way to check if a SavingAccount is to use isinstance:

        def transfer_to_saving(self, amount):
            if not isinstance(self.saving_account, SavingAccount):
                print('Must create a saving account')
            elif amount > self.balance:
                print('Insufficient funds')
            else:
                self.saving_account.balance += amount
                self.balance -= amount