1

I have deposit, withdraw and transfer methods in my OOP banking app that seems to be printing correct values, although if I remove the repr method it shows that 1 ID is not identical.

EDIT: My other classes, not sure if relevant here:

class PersonError(Exception):
    """Person error."""
    pass


class TransactionError(Exception):
    """Transaction error."""
    pass


class Person:
    """Person class."""

    def __init__(self, first_name: str, last_name: str, age: int):
        """
        Person constructor.

        :param first_name: first name
        :param last_name: last name
        :param age: age, must be greater than 0
        """
        if not first_name or not last_name:
            raise PersonError
        else:
            self.first_name = first_name
            self.last_name = last_name

        self.bank_account = None
        if age <= 0:
            raise PersonError
        else:
            self._age = age

    @property
    def full_name(self) -> str:
        """Get person's full name. Combination of first and last name."""
        return f"{self.first_name} {self.last_name}"

    @property
    def age(self) -> int:
        """Get person's age."""
        return self._age

    @age.setter
    def age(self, value: int):
        """Set person's age. Must be greater than 0."""
        if value <= 0:
            raise PersonError
        else:
            self._age = value

    def __repr__(self) -> str:
        """
        Person representation.

        :return: person's full name
        """
        return self.full_name


class Bank:
    """Bank class."""

    def __init__(self, name: str):
        """
        Bank constructor.

        :param name: name of the bank
        """
        self.name = name
        self.customers = []
        self.transactions = []


    def add_customer(self, person: Person) -> bool:
        """
        Add customer to bank.

        :param person: person object
        :return: was customer successfully added
        """
        if person not in self.customers:
            self.customers.append(person)
            person.bank_account = Account(0, person, bank)
            return True
        else:
            return False

    def remove_customer(self, person: Person) -> bool:
        """
        Remove customer from bank.

        :param person: person object
        :return: was customer successfully removed
        """
        if person in self.customers:
            self.customers.remove(person)
            person.bank_account = None
            return True
        else:
            return False

    def __repr__(self) -> str:
        """
        Bank representation.

        :return: name of the bank
        """
        return self.name


class Transaction:
    """Transaction class."""

    def __init__(self, amount: float, date: datetime.date, sender_account: 'Account', receiver_account: 'Account',
                 is_from_atm: bool):
        """
        Transaction constructor.

        :param amount: value
        :param date: date of the transaction
        :param sender_account: sender's object
        :param receiver_account: receiver's object
        :param is_from_atm: is transaction from atm
        """
        self.is_from_atm = is_from_atm
        self.receiver_account = receiver_account
        self.sender_account = sender_account
        self.date = date
        self.amount = amount

    def __repr__(self) -> str:
        """
        Transaction representation.

        :rtype: object's values displayed in a nice format
        """
        if self.is_from_atm is True:
            return f"({self.amount} €) ATM"
        elif self.is_from_atm is False:
            return f"({self.amount} €) {self.sender_account.person} -> {self.receiver_account.person}"



My methods:

class Account:
    """Account class."""

    def __init__(self, balance: float, person: Person, bank: 'Bank'):
        """
        Account constructor.

        :param balance: initial account balance
        :param person: person object
        :param bank: bank object
        """
        self.bank = bank
        self.person = person
        self._balance = balance
        self.number = f"EE{randint(111111111111111111, 999999999999999999)}"
        self.transactions = []

    @property
    def balance(self) -> float:
        """Get account's balance."""
        return self._balance

    def deposit(self, amount: float, is_from_atm: bool = True):
        """Deposit money to account."""
        if amount <= 0:
            raise TransactionError
        elif is_from_atm is True:
            t = Transaction(amount, date=datetime.date.today(), sender_account=self, receiver_account=self, is_from_atm=True)
            self._balance = self._balance + amount
            self.transactions.append(t)
            bank.transactions.append(t)
        elif is_from_atm is False:

            self._balance = self._balance + amount

    def withdraw(self, amount: float, is_from_atm: bool = True):
        """Withdraw money from account."""
        if amount <= 0 or amount > self._balance:
            raise TransactionError
        elif is_from_atm is True:
            t = Transaction(-amount, date=datetime.date.today(), sender_account=self, receiver_account=self, is_from_atm=True)
            self._balance = self._balance - amount
            self.transactions.append(t)
            bank.transactions.append(t)
        elif is_from_atm is False:

            self._balance = self._balance - amount

    def transfer(self, amount: float, receiver_account: 'Account'):
        """Transfer money from one account to another."""
        fee = 5
        if receiver_account == self:
            raise TransactionError
        elif (self._balance - fee - amount) < 0:
            raise TransactionError
        elif receiver_account.bank != self.bank and amount >= 0:
            t = Transaction(amount, datetime.date.today(), sender_account=self, receiver_account=receiver_account,
                            is_from_atm=False)
            self._balance = self._balance - amount - fee
            self.transactions.append(t)
            self.bank.transactions.append(t)

            t = Transaction(amount, datetime.date.today(), sender_account=self, receiver_account=receiver_account,
                            is_from_atm=False)
            receiver_account._balance = receiver_account._balance + amount
            receiver_account.transactions.append(t)
            receiver_account.bank.transactions.append(t)
        elif receiver_account.bank == self.bank and amount >= 0:
            t = Transaction(amount, datetime.date.today(), sender_account=self, receiver_account=receiver_account,
                            is_from_atm=False)
            self._balance = self._balance - amount
            self.transactions.append(t)
            self.bank.transactions.append(t)

            t = Transaction(amount, datetime.date.today(), sender_account=self, receiver_account=receiver_account,
                            is_from_atm=False)
            receiver_account._balance = receiver_account._balance + amount
            receiver_account.transactions.append(t)

If name is main :


if __name__ == '__main__':
    person = Person("Ago", "Luberg", 100)
    
    bank = Bank("TalBank")
    bank.add_customer(person)
    a1 = person.bank_account

    person2 = Person("Mati", "Maal", 90)
    bank.add_customer(person2)
    a2 = person2.bank_account
    
    a1.deposit(100)
    a2.deposit(100)
    a1.withdraw(10)
    a2.withdraw(50)
    a1.transfer(10, a2)
    
    
    print(a1.transactions)  # [(100 €) ATM, (-10 €) ATM, (10 €) Ago Luberg -> Mati Maal]
    print(a2.transactions)  # [(100 €) ATM, (-50 €) ATM, (10 €) Ago Luberg -> Mati Maal]
    print(bank.transactions)
    # [(100 €) ATM, (100 €) ATM, (-10 €) ATM, (-50 €) ATM, (10 €) Ago Luberg -> Mati Maal]

My output without repr when printing transactions:

1. person1 transactions [<__main__.Transaction object at 0x0000024AE1ACF4F0>, <__main__.Transaction object at 0x0000024AE1ACF490>, <__main__.Transaction object at 0x0000024AE1ACF340>]
2. person2 transactions [<__main__.Transaction object at 0x0000024AE1ACF520>, <__main__.Transaction object at 0x0000024AE1ACF400>, <__main__.Transaction object at 0x0000024AE1ACF2B0>]
3. bank transactions(person1 + person2 transactions) [<__main__.Transaction object at 0x0000024AE1ACF4F0>, <__main__.Transaction object at 0x0000024AE1ACF520>, <__main__.Transaction object at 0x0000024AE1ACF490>, <__main__.Transaction object at 0x0000024AE1ACF400>, <__main__.Transaction object at 0x0000024AE1ACF340>]

Expected output without repr method:

1. person1 transactions [<__main__.Transaction object at 0x0000024AE1ACF4F0>, <__main__.Transaction object at 0x0000024AE1ACF490>, <__main__.Transaction object at 0x0000024AE1ACF340>]
2. person2 transactions [<__main__.Transaction object at 0x0000024AE1ACF520>, <__main__.Transaction object at 0x0000024AE1ACF400>, <__main__.Transaction object at 0x0000024AE1ACF340>]
3. bank transactions(person1 + person2 transactions) [<__main__.Transaction object at 0x0000024AE1ACF4F0>, <__main__.Transaction object at 0x0000024AE1ACF520>, <__main__.Transaction object at 0x0000024AE1ACF490>, <__main__.Transaction object at 0x0000024AE1ACF400>, <__main__.Transaction object at 0x0000024AE1ACF340>]

My question is why is the last transaction object in person2 transactions not identical to the last transaction in person1 and bank transactions although with repr method they are identical.

Output with repr method :

[(100 €) ATM, (-10 €) ATM, (10 €) Ago Luberg -> Mati Maal]
[(100 €) ATM, (-50 €) ATM, (10 €) Ago Luberg -> Mati Maal]
[(100 €) ATM, (100 €) ATM, (-10 €) ATM, (-50 €) ATM, (10 €) Ago Luberg -> Mati Maal]

Thanks in advance to whomever wishes to contribute with their help! Crimson.

4
  • 2
    Why do you assume the identity should be the same? The __repr__ methods outputs an arbitrary string. It has no bearing on object identity. Commented Nov 17, 2020 at 13:31
  • 2
    Also, strongly consider to build a minimal reproducible example – emphasis on minimal. This "issue" can be shown in a dozen lines of code. There is no need to throw almost 250 lines of code at people. Commented Nov 17, 2020 at 13:32
  • I assume the identity should be the same, since I am appending the exact same object in my transfer method within Account class. t = Transaction(etc). From my understanding they should be identical Commented Nov 17, 2020 at 13:32
  • I was unsure if i could build a minimal reproducdible example of this, my apologies. I am quite new to Python and SO community, thanks for your feedback! Commented Nov 17, 2020 at 13:33

1 Answer 1

2

In this portion of the code:

elif receiver_account.bank != self.bank and amount >= 0:
            t = Transaction(amount, datetime.date.today(), sender_account=self, receiver_account=receiver_account,
                            is_from_atm=False)
            self._balance = self._balance - amount - fee
            self.transactions.append(t)
            self.bank.transactions.append(t)

            t = Transaction(amount, datetime.date.today(), sender_account=self, receiver_account=receiver_account,
                            is_from_atm=False)
            receiver_account._balance = receiver_account._balance + amount
            receiver_account.transactions.append(t)
            receiver_account.bank.transactions.append(t)

You are declaring t twice: declaring it one time, then appending it to self.transactions, then declaring it another time (so it becomes equal to another object reference) and appending it to receiver_account. So t the first time and the second time is not the same object.

Removing the second declaration of t should give your expected result.

Sign up to request clarification or add additional context in comments.

3 Comments

Should I remove the entire t = Transaction or just the t in front of Transaction? Thanks in regards.
The entire (second) line, as you do not need two different objects holding the same value
Thank you kind sir, before designating this as the best answer, would you be willing to tell me as to why this code could be printing a NameError: name 'bank' is not defined? Best regards, Crimson

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.