Recap - Bank Account Manager
Part of the Object Oriented Programming section of Coddy's Python journey — lesson 16 of 64.
Challenge
MediumCreate a complete BankAccount class that demonstrates object-oriented programming concepts including class variables, private attributes, properties, getters, setters, and methods.
1. Create a BankAccount class with:
- A class variable
interest_rateset to0.02(2%) - Private instance attributes for
__owner_nameand__balance - A constructor that initializes these attributes
2. Implement property getters and setters:
- A read-only property
owner_namethat returns the account owner's name - A property
balancewith:- A getter that returns the current balance
- A setter that validates deposits (rejecting negative amounts)
- If validation fails, print:
"Balance cannot be negative"
3. Implement methods:
deposit(amount)that:- Adds to the balance if amount is positive
- Prints
"Deposit amount must be positive"and returnsFalseif amount is not positive - Returns
Trueon success
withdraw(amount)that:- Subtracts from the balance if amount is positive and funds are sufficient
- Prints
"Withdrawal amount must be positive"and returnsFalseif amount is not positive - Prints
"Insufficient funds"and returnsFalseif amount exceeds balance - Returns
Trueon success
apply_interest()that:- Adds interest to the balance based on the class interest rate
- Returns the interest amount
display_info()that prints account details in this format:Account Owner: [owner_name] Balance: $[balance] Interest Rate: [interest_rate]%
Output Format Requirements
- All validation error messages must be printed exactly as specified
- The
display_info()method must format the output exactly as shown above - Interest rate should be displayed as a percentage (multiply by 100)
Cheat sheet
A complete class demonstrating object-oriented programming concepts:
class BankAccount:
interest_rate = 0.02 # Class variable
def __init__(self, owner_name, initial_balance):
self.__owner_name = owner_name # Private attribute
self.__balance = initial_balance # Private attribute
@property
def owner_name(self): # Read-only property
return self.__owner_name
@property
def balance(self): # Getter
return self.__balance
@balance.setter
def balance(self, value): # Setter with validation
if value < 0:
print("Balance cannot be negative")
else:
self.__balance = value
def deposit(self, amount):
if amount <= 0:
print("Deposit amount must be positive")
return False
self.__balance += amount
return True
def withdraw(self, amount):
if amount <= 0:
print("Withdrawal amount must be positive")
return False
if amount > self.__balance:
print("Insufficient funds")
return False
self.__balance -= amount
return True
def apply_interest(self):
interest = self.__balance * self.interest_rate
self.__balance += interest
return interest
def display_info(self):
print(f"Account Owner: {self.__owner_name}")
print(f"Balance: ${self.__balance}")
print(f"Interest Rate: {self.interest_rate * 100}%")
Key concepts:
- Class variables: Shared across all instances
- Private attributes: Use double underscore prefix (
__) - Properties: Use
@propertydecorator for getters - Setters: Use
@property_name.setterdecorator - Validation: Check conditions and provide error messages
Try it yourself
from bank_account import BankAccount
# Test case handler
test_case = input()
if test_case == "account_creation":
# Test account creation
account = BankAccount("Alice", 1000)
print(f"Owner: {account.owner_name}")
print(f"Initial balance: ${account.balance}")
print(f"Interest rate: {BankAccount.interest_rate * 100}%")
elif test_case == "deposit_method":
# Test deposit functionality
account = BankAccount("Bob", 500)
print(f"Initial balance: ${account.balance}")
# Valid deposit
result = account.deposit(300)
print(f"Deposit result: {result}")
print(f"New balance: ${account.balance}")
# Invalid deposit
result = account.deposit(-50)
print(f"Negative deposit result: {result}")
print(f"Balance after invalid deposit: ${account.balance}")
elif test_case == "withdraw_method":
# Test withdraw functionality
account = BankAccount("Charlie", 1000)
# Valid withdrawal
result = account.withdraw(400)
print(f"Withdrawal result: {result}")
print(f"Balance after withdrawal: ${account.balance}")
# Invalid withdrawal (negative)
result = account.withdraw(-50)
print(f"Negative withdrawal result: {result}")
# Invalid withdrawal (exceeds balance)
result = account.withdraw(1000)
print(f"Excessive withdrawal result: {result}")
print(f"Final balance: ${account.balance}")
elif test_case == "property_access":
# Test property access and protection
account = BankAccount("Dave", 800)
print(f"Owner via property: {account.owner_name}")
print(f"Balance via property: ${account.balance}")
# Try to set balance (should use the setter)
account.balance = 1200
print(f"Balance after valid setter: ${account.balance}")
# Try invalid balance
account.balance = -500
print(f"Balance after invalid setter: ${account.balance}")
# Try to access private attributes directly (should fail)
try:
print(account.__owner_name)
except AttributeError:
print("Cannot access private owner_name directly")
elif test_case == "apply_interest":
# Test interest application
account = BankAccount("Eve", 2000)
# Check original balance
print(f"Initial balance: ${account.balance}")
# Apply interest
interest_earned = account.apply_interest()
expected_interest = 2000 * 0.02
print(f"Interest earned: ${interest_earned}")
print(f"New balance after interest: ${account.balance}")
print(f"Interest calculation correct: {interest_earned == expected_interest}")
elif test_case == "display_info":
# Test display_info method format
account = BankAccount("Frank", 1500)
# Modify interest rate to test class variable
original_rate = BankAccount.interest_rate
BankAccount.interest_rate = 0.03
print("Display info output:")
account.display_info()
# Restore original rate
BankAccount.interest_rate = original_rate
elif test_case == "original_test_case":
# Run the original test code from the challenge
account = BankAccount("Coddy", 1000)
# Perform operations
account.deposit(500)
account.withdraw(200)
account.apply_interest()
# Display account information
account.display_info()
# Test setter validation
account.balance = 5000
print(f"Balance after setter: ${account.balance}")
# Test withdrawal validation
account.withdraw(10000)
print(f"Final balance: ${account.balance}")All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe self ParameterMethodsAttributesConstructor Method (__init__)Recap - Simple Calculator4Inheritance
Basic InheritanceThe super() FunctionMethod OverridingMultiple InheritanceMethod Resolution OrderRecap - Employee Hierarchy7Special Methods
Magic Methods IntroductionOperator OverloadingContainer Magic MethodsRecap - Custom List10Design Patterns Part 1
Intro to design patternSingleton PatternFactory PatternObserver PatternStrategy Pattern2Decorators
Introduction to DecoratorsProperty DecoratorStatic Method DecoratorClass Method Decorator5Polymorphism
Method Overriding RevisitedDuck TypingAbstract Classes and MethodsInterface DesignRecap - Shape Calculator8Advanced OOP Concepts
Composition vs InheritanceMixinsStatic and Class MethodsClass DecoratorsContext Managers3Class Properties
Instance vs Class VariablesProperty DecoratorsPrivate AttributesRecap - Bank Account Manager6Encapsulation
Public, Protected, Private MemAccess ModifiersInformation HidingProperty Decorators AdvancedRecap - Student Records System12Project: Library Management
Project OverviewBook and User Classes