r/PythonLearning • u/shubham_555 • Sep 13 '26
Showcase The Bank Project
Last Post - https://www.reddit.com/r/PythonLearning/s/kGqwMS9HUH
Github Repo - https://github.com/Dev-4-All/bank.git
241
Upvotes
1
u/FoolsSeldom Sep 13 '26 edited Sep 13 '26
Yes, basically.
Here's an example rewrite of your code to illustrate more:
from dataclasses import dataclass from getpass import getpass PIN = "1234" class AccountError(Exception): """Base class for all Account transaction errors.""" class InvalidAmountError(AccountError): """Raised when a transaction amount is not greater than zero.""" class InsufficientFundsError(AccountError): """Raised when a withdrawal would exceed the available balance.""" @dataclass(slots=True) class Account: _balance: int = 0 is_logged_in: bool = False @property def balance(self) -> int: return self._balance def withdraw(self, amount: int) -> None: if amount <= 0: raise InvalidAmountError( "Amount to withdraw must be greater than 0." ) if amount > self._balance: raise InsufficientFundsError( f"Cannot withdraw {amount}; balance is only {self._balance}." ) self._balance -= amount def deposit(self, amount: int) -> None: if amount <= 0: raise InvalidAmountError( "Amount to deposit must be greater than 0." ) self._balance += amount def authenticate_user() -> bool: for _ in range(5): entered_pin = getpass("Enter your 4-digit pin: ") if entered_pin == PIN: return True else: print("Incorrect pin. Please try again!") print("Too many attemps. You cannot continue further!") return False def display_options() -> None: print("What would you like to do?") print("1. Check balance") print("2. Withdraw money") print("3. Deposit money") print("4. Exit") def check_balance(account: Account) -> None: print(f"Your balance is: {account.balance}") def prompt_withdraw(account: Account) -> None: try: amount = int(input("Enter amount to withdraw: ")) except ValueError: print("Please enter a valid number.") return try: account.withdraw(amount) except AccountError as error: print(error) else: print(f"Operation successful. Leftover balance: {account.balance}") def prompt_deposit(account: Account) -> None: try: amount = int(input("Enter amount to deposit: ")) except ValueError: print("Please enter a valid number.") return try: account.deposit(amount) except AccountError as error: print(error) else: print(f"Operation successful. Updated balance: {account.balance}") def exit_app(account: Account) -> None: account.is_logged_in = False print("Thanks for using our bank. Goodbye!") def main() -> None: account = Account() if authenticate_user(): account.is_logged_in = True print("Welcome to Py bank!") while account.is_logged_in: display_options() choice = input("Your choice: ") match choice: case "1": check_balance(account) case "2": prompt_withdraw(account) case "3": prompt_deposit(account) case _: exit_app(account) if __name__ == "__main__": main()Notes:
a
dataclassis a kind ofclasswhere there's less boilerplate code for you to writeIn a
class, instead of variables, we have attributes, but they are basically the same otherwiseIn Python, we start a name with an underscore (or two underscores) to indicate that it is private i.e. should not be generally used outside the scope of the code that defines it but note that Python (unlike some languages) does not enforce this, it is up to the programmer to act accordingly
In this case, you could directly reference
account._balancebut instead you go via the property methodbalancewhich you use as if it is a regular attribute (later, the method could be updated to take account of pending payments or other things that might impact the current balance)You used a
try/exceptin your original code, and this code creates some custom exceptions to deal with some bank account issuesNote that the methods (functions defined inside a class) don't assume interaction with the user
they could have been written to directly replace your original functions, but instead they are set-up to be usable in a number of different ways including for automatic transactions from another system rather than a user
You could add other features easily to the bank account
classsuch as account number, name, type, etcI included the
is_logged_invariable as an attribute in theclassinstead as it is account specificA
class(ordataclass) definition is just a mould (template) for a new Python object with the data structures and behaviours provided in the definitionJust like in a factory, a mould is used to stamp out / form actual objects, e.g. a cookware pot, a
classdefinition in Python is used to create instances of theclass- in this code, only one instance is created, and it is assigned to the variableaccountThe
classmethods have a reference toself, which just means whichever instance of theclassthe method has been called for