(a) The definition of BankAccount
in the code that follows uses the Luhn checksum method to validate the account number.
The account number may be provided as an integer or as a string (possibly with spaces or hyphens delimiting groups of numbers):
>>> from bank_account2 import BankAccount
>>> BankAccount('John Brown', 7298-904')
<bank_account2.BankAccount at 0x103fc1350>
>>> BankAccount('John Brown', 7298905)
...
ValueError: Invalid account number
(b) An interest-free overdraft is implemented in the CurrentAccount
class below as well; a change has been made to the base class's BankAccount
method to output negative balances cleanly.
>>> from bank_account2 import CurrentAccount
>>> account = CurrentAccount('John Brown', '729-8904', 0, 500)
>>> account.deposit(400)
>>> account.withdraw(350)
>>> account.check_balance()
The balance of account number 729-8904 is $50.00
>>> account.withdraw(200)
>>> account.check_balance()
The balance of account number 729-8904 is -$150.00
>>> account.withdraw(600)
$600.00 exceeds the single transaction limit of $500.00
>>> account.withdraw(500)
>>> account.check_balance()
The balance of account number 729-8904 is -$650.00
>>> account.withdraw(500)
Insufficient funds
The modified code is:
# bank_account2.py
class BankAccount:
""" A abstract base class representing a bank account."""
currency = '$'
def __init__(self, customer, account_number, balance=0):
"""
Initialize the BankAccount class with a customer, account number
and opening balance (which defaults to 0.)
"""
self.customer = customer
self.set_account_number(account_number)
self.balance = balance
def set_account_number(self, account_number):
if not self.verify_account_number(account_number):
raise ValueError('Invalid account number')
self.account_number = account_number
def verify_account_number(self, account_number):
""" Verify the account number, returning True if it is valid. """
ccl = [int(d) for d in str(account_number) if d not in ('-', ' ')]
for i in range(len(ccl)-2, 0, -2):
ccl[i] *= 2
if ccl[i] > 9:
ccl[i] = 1 + (ccl[i] - 10)
checksum = sum(ccl) % 10
return not checksum
def deposit(self, amount):
""" Deposit amount into the bank account."""
if amount > 0:
self.balance += amount
else:
print('Invalid deposit amount:', amount)
def withdraw(self, amount):
"""
Withdraw amount from the bank account, ensuring there are sufficient
funds.
"""
if amount > 0:
if amount > self.balance:
print('Insufficient funds')
else:
self.balance -= amount
else:
print('Invalid withdrawal amount:', amount)
def check_balance(self):
""" Print a statement of the account balance. """
s_sign = '-' if self.balance < 0 else ''
print('The balance of account number {} is {}{}{:.2f}'
.format(self.account_number, s_sign, self.currency,
abs(self.balance)))
class CurrentAccount(BankAccount):
""" A class representing a current (checking) account. """
def __init__(self, customer, account_number, annual_fee,
transaction_limit, balance=0, overdraft_limit=1000):
""" Initialize the current account. """
self.annual_fee = annual_fee
self.transaction_limit = transaction_limit
self.overdraft_limit = overdraft_limit
super().__init__(customer, account_number, balance)
def withdraw(self, amount):
"""
Withdraw amount if sufficient funds exist in the account and amount
is less than the single transaction limit.
"""
if amount <= 0:
print('Invalid withdrawal amount:', amount)
return
if amount > self.balance + self.overdraft_limit:
print('Insufficient funds')
return
if amount > self.transaction_limit:
print('{0:s}{1:.2f} exceeds the single transaction limit of'
' {0:s}{2:.2f}'.format(self.currency, amount,
self.transaction_limit))
return
self.balance -= amount
def apply_annual_fee(self):
""" Deduct the annual fee from the account balance. """
self.balance = max(0., self.balance - self.annual_fee)