Inheritance and Printing in Bank account in python - python

This is a section from my bank account code, i am new to OOP and have added some inheritance to my code, the trouble i am having is with printing the balance with interest, when i print it I get the same value as the regular balance without interest, give your insight.
from random import randint
class BankAccount(object):
def __init__(self, initial_balance=0):
self.balance = initial_balance
def deposit(self, amount):
self.balance += amount
def withdraw(self, amount):
self.balance -= amount
def get_balance(self, initial_balance, rate):
return self.get_balance() * self._rate
class BankAccountWithInterest(BankAccount):
def __init__(self, initial_balance=0, rate=0.1):
BankAccount.__init__(self, initial_balance)
self._rate = rate
def interest(self):
return self.balance * self._rate
balance = (randint(100, 500))
my_account = BankAccount(balance)
my_interest = BankAccountWithInterest(balance)
print(my_account.balance)
print(my_interest.balance)

You have a couple of problems here, the main one being that you don't ever apply (call) the interest function, which is why you get the same balance for both. You should do:
print(my_account.balance)
print(my_interest.balance + my_interest.interest())
I would also recommend changing self._rate to rate in the get_balance method, otherwise you may get an error when you call get_balance and it tries to access self._rate. You should also have it return self.balance or self.balance + rate otherwise you are getting an infinite loop.

print(my_interest.balance + my_interest.interest())
Seems to return what you expect.
my_interest.balance = my_interest.balance + my_interest.interest()
print(my_interest.balance)
Will also update the balance with the added interest.

Related

Cant finish a code, __init__() takes from 1 to 2 positional arguments but 3 were given

Hello i want to write a test program that creates an Account object with an account id of 1122, a balance of $20,000,and an annual interest rate of 4.5%. Use the withdraw method to withdraw $2,500, use the deposit method to deposit$3,000, and print the id, balance, monthly interest rate, and monthly interest but i keep getting
TypeError: __init__() takes from 1 to 2 positional arguments but 4 were given "
Here is how my code in file with class looks like
class Account:
def __init__(self,id=0,balance=20000,annualInterestRate=4.5):
self.__id = id
def getid(self):
return self.__id
def getbalance(self):
return self.__balance
def getannualInterestRate(self):
return self.__annualInterestRate
def setid(self,id):
self.__id = id
def setbalance(self, balance):
self.__balance = balance
def setannualInterestRate(self, rate):
self.__annualInterestRate = rate
def getMonthlyInterestRate(self):
return self.__annualInterestRate/12
def getMonthlyInterest(self):
return self.__balance * self.getMonthlyInterestRate()
def withdraw(self, amount):
if amount <= self.__balance:
self.__balance -= amount
return True
else:
return False
def deposit(self, amount):
self.__balance +=amount
return balance
and this is how i try to initialize it
from Acc import Account
def main():
updatedAccount = Account(1222,20000,4.5)
updatedAccount.withdraw(2500)
print("User ID : ", updatedAccount.getid())
print("Beginning Balance: ", updatedAccount.getbalance())
print("Monthly Interest Rate: ", updatedAccount.getMonthlyInterestRate())
print("Monthly Interest: ", updatedAccount.getMonthlyInterest())
main()
Can someone please help with the last part of a code where i try to initialize it
Just a thought, why don't you initialize the balance and interest in the init() body?
Then use the setbalance or setters to set the values when the user wants to change the initial value?
Answer to your question: You provided the values to the constructor as positional arguments. Whenever you use the keyword(kwargs) argument construct, remember to explicitly set them in the function call.
Example:
def do_some(i=34, j=23):
Function call:
do_some(i=23,i=12)
This is how your definition of Account should look:
class Account:
# No need for a default id; it should be required
# 0 is a better default for balance; banks don't give you $20,000 unless you specify otherwise :)
def __init__(self, id, initial_balance=0, rate=4.5):
self.id = id
self.balance = initial_balance
self.annual_interest_rate = rate
# Ditch the unnecessary getters and setters
# Example of a "computed attribute value", rather than an explicit method
#property
def monthly_interest_rate(self):
return self.annual_interest_rate / 12
def get_monthly_interest(self):
return self.balance * self.monthly_interest_rate
# Don't return True/False. Do the debit, or raise an exception
def withdraw(self, amount):
if self.balance < amount:
raise ValueError(f"Overdraft, balance less than {amount}")
self.balance -= amount
# Don't return the balance; mutating methods should return None.
# The caller can always check self.balance after the deposit
def deposit(self, amount):
self.balance +=amount
Example usage:
from Acc import Account
def main():
updatedAccount = Account(1222,20000,4.5)
updatedAccount.withdraw(2500)
print("User ID : ", updatedAccount.id)
print("Beginning Balance: ", updatedAccount.balance)
print("Monthly Interest Rate: ", updatedAccount.monthly_interest_rate)
print("Monthly Interest: ", updatedAccount.get_monthly_interest())
main()

Why does this class program not return the balance?

Question: Develop a class BankAccount that supports these methods:
__init__(): Initializes the bank account balance to the value of the input argument or to 0 if no input argument is given
withdraw(): Take an argument as an input and withdraws it from the balance
deposit(): Take an amount as an input and add it to the balance
balance(): Returns the balance on the account
class ValueErrorException (Exception):
pass
class BankAccount:
accounts = 0
def __init__ (self, bal = 0.0):
BankAccount.accounts += 1
self.accountNumber = str(BankAccount.accounts)
self.balance = bal
def withdraw(self, amount):
if self.balance - amount < 0:
raise ValueErrorException("Illegal balance")
else:
self.balance -= amount
def deposit (self, amount):
self.balance += amount
def balance(self, amount):
return amount
The balance definition should be like this:
def balance(self):
return self.balance
You may also want to consider changing the variable name from balance to accountBalance so it doesn't affect the definition that is named the same. Your new code would now be:
class ValueErrorException (Exception):
pass
class BankAccount:
accounts = 0
def __init__ (self, bal = 0.0):
BankAccount.accounts += 1
self.accountNumber = str(BankAccount.accounts)
self.accountBalance = bal
def withdraw(self, amount):
if self.accountBalance - amount < 0:
raise ValueErrorException("Illegal balance")
else:
self.accountBalance -= amount
def deposit (self, amount):
self.accountBalance += amount
def balance(self):
return self.accountBalance
return self.balance
access the classes instance variable, not a functional argument. no need to pass amount to a function just to return it

TypeError: unsupported operand type(s) for -: 'float' and 'method'

So guys, I'm trying to work some exercises in Python after reading about classes and objects, and one of said exercises is to create an Account Class and write methods to withdraw and deposit money into an account. Every time I run it, I get a TypeError telling me that the operand is unsupported for Floats and Methods. I feel like I'm close but missing something really obvious. Could anyone give me an idea as to what I'm doing wrong and how to fix this?
class Account:
def __init__(account, id, balance, rate):
account.__id = id
account.__balance = float(balance)
account.__annualInterestRate = float(rate)
def getMonthlyInterestRate(account):
return (account.__annualInterestRate/12)
def getMonthlyInterest(account):
return account.__balance*(account.__annualInterestRate/12)
def getId(account):
return account.__id
def getBalance(account):
return account.__balance
def withdraw(account, balance):
return (account.__balance) - (account.withdraw)
def deposit(account, balance):
return (account.__balance) + (account.deposit)
def main():
account = Account(1122, 20000, 4.5)
account.withdraw(2500)
account.deposit(3000)
print("ID is " + str(account.getId()))
print("Balance is " + str(account.getBalance()))
print("Monthly interest rate is" + str(account.getMonthlyInterestRate()))
print("Monthly interest is " + str(account.getMonthlyInterest()))
main()
This:
def withdraw(account, balance):
return (account.__balance) - (account.withdraw)
Should look something like this:
def withdraw(self, amount):
self.__balance -= amount
In Python we always refer to the class inside methods as self.
Applying this to the rest of the class and cleaning some things up:
class Account:
def __init__(self, id, balance, rate):
self.id = id
self.balance = float(balance)
self.annualInterestRate = float(rate)
def getMonthlyInterestRate(self):
return self.annualInterestRate / 12
def getMonthlyInterest(self):
return self.balance * self.getMonthlyInterestRate()
def getId(self):
return self.id
def getBalance(self):
return self.balance
def withdraw(self, amount):
self.balance -= amount
def deposit(self, amount):
self.balance += amount

TypeError : object does not support indexing

I recently learned Python. I can't catch the error in this code. What is wrong?
class BankAccount:
def __init__(self, initial_balance):
"""Creates an account with the given balance."""
self = [initial_balance, 0]
def deposit(self, amount):
"""Deposits the amount into the account."""
self += amount
def withdraw(self, amount):
"""
Withdraws the amount from the account. Each withdrawal resulting in a
negative balance also deducts a penalty fee of 5 dollars from the balance.
"""
self[0] -= amount
if self[0] < 0:
self[0] -= 5
self[1] += 1
def get_balance(self):
"""Returns the current balance in the account."""
return self[0]
def get_fees(self):
"""Returns the total fees ever deducted from the account."""
return 5*self[1]
my_account = BankAccount(10)
my_account.withdraw(15)
my_account.deposit(20)
my_account.get_balance(), my_account.get_fees()
The error is:
Traceback (most recent call last):
File "C:\Python34\bank.py", line 28, in <module>
my_account.withdraw(15)
File "C:\Python34\bank.py", line 15, in withdraw
self[0] -= amount + 5
TypeError: 'BankAccount' object does not support indexing
self value contains initial_balance and a count of how many withdrawals have happened.
self always refers to the object that calls the class function. So, it is recommended not to assign something to the self variable like:
self = #something
in the constructor. Give that variable a name. Like:
self.details = [initialbalance,0]
And use the variable name wherever.
It should look something like this:
class BankAccount:
OVERDRAW_PENALTY = 5
def __init__(self, opening_balance=0):
self.balance = opening_balance
self.withdrawals = 0
def withdraw(self, amount):
self.withdrawals += 1
self.balance -= amount
if self.balance < 0:
self.balance -= self.OVERDRAW_PENALTY
return amount
Note that I am using self to access instance and class attributes, not trying to assign directly to it. Also, I have factored out the "magic number" 5, so it is clearer what is happening.
Also, your implementation of get_fees is incorrect - the number of withdrawals is currently incremented whether or not a fee is applied. You should either store a self.overdrawn_withdrawals count separately or keep a running total self.fees attribute.
Finally, I have added return amount to the end of withdraw - this allows you to do things like:
account2.deposit(account1.withdraw(100))
to easily transfer money between BankAccounts.
self refers to the BankAccount object. You need to assign and reference instance variables.
Your __init__() method should be something like this:
class BankAccount:
def __init__(self, initial_balance):
"""Creates an account with the given balance."""
self.balance = initial_balance
self.fees = 0
self.withdrawals = 0
def deposit(self, amount):
"""Deposits the amount into the account."""
self.balance += amount
Similarly withdraw() and get_balance() should reference self.balance, and get_fees() should reference self.fees.

Bank account Classes. How to make it more powerful/efficient? (OOP)

I want to make this program work without using global variables. Basically, if the person withdraws and ends up going in negative figures, they incur a penalty of 5. Every transaction that's in minus will also cause a penalty of 5.
The program seems to work fine assuming there are only 2 accounts:
account1 = Bankaccount(20)
account2 = Bankaccount(5)
but thats its limitation. How can I allow infinite accounts? So I don't get constrained to the two globals. I hope this makes sense, assume I have to change withdrawal function and get_fees, but I'm new to OOP so I'm stuck. Thanks for your help!
pen = 0
pen2 = 0
class BankAccount:
def __init__(self, initial_balance):
"""Creates an account with the given balance."""
self.money = initial_balance
def deposit(self, amount):
"""Deposits the amount into the account."""
self.money += amount
return self.money
def withdraw(self, amount):
"""
Withdraws the amount from the account. Each withdrawal resulting in a
negative balance also deducts a penalty fee of 5 dollars from the balance.
"""
global pen, pen2
penalty = 5
if self.money - amount < 0:
self.money -= (amount + penalty)
if self == account1:
pen += 5
elif self == account2:
pen2 += 5
else:
self.money -= amount
return self.money
def get_balance(self):
"""Returns the current balance in the account."""
return self.money
def get_fees(self):
"""Returns the total fees ever deducted from the account."""
global pen, pen2
if self == account1:
return pen
elif self == account2:
return pen2
Make the penalty a instance attribute, just as money is:
def __init__(self, initial_balance):
"""Creates an account with the given balance."""
self.money = initial_balance
self.penalty = 0
And, later on:
def withdraw(self, amount):
if self.money - amount < 0:
self.penalty += 5
Just turn the global variables into instance variables:
class BankAccount:
PENALTY = 5
def __init__(self, initial_balance):
"""Creates an account with the given balance."""
self.money = initial_balance
self.penalty = 0
def deposit(self, amount):
"""Deposits the amount into the account."""
self.money += amount
return self.money
def withdraw(self, amount):
"""
Withdraws the amount from the account. Each withdrawal resulting in a
negative balance also deducts a penalty fee of 5 dollars from the balance.
"""
if self.money - amount < 0:
self.money -= (amount + BankAccount.PENALTY)
self.penalty += BankAccount.PENALTY
else:
self.money -= amount
return self.money
def get_balance(self):
"""Returns the current balance in the account."""
return self.money
def get_fees(self):
"""Returns the total fees ever deducted from the account."""
return self.penalty
I also noticed you declared a variable called penalty in the withdraw function. This looks like the beginning of good practice in avoiding magic numbers, so I continued along those lines by making it a constant attribute of the BankAccount class.
Also, in Python we don't usually use functions just to access attributes. Instead of bobs_account.get_fees() it would be more normal to do bobs_account.penalty.
The penalty should also be an instance variable of a BankAccount object:
class BankAccount:
penalty_amount = 5
def __init__(self, initial_balance):
self.money = initial_balance
self.penalty = 0
def withdraw(self, amount):
"""
Withdraws the amount from the account. Each withdrawal resulting in a
negative balance also deducts a penalty fee of 5 dollars from the balance.
"""
if self.money - amount < 0:
self.money -= (amount + BankAccount.penalty_amount)
self.penalty += BankAccount.penalty_amount
else:
self.money -= amount
return self.money
class BankAccount:
accounts = {}
def __init__(self,account_id, initial_balance):
BankAccount.accounts[account_id] = self
...
#classmethod
def get_account(cls,acct_id):
return BankAccount.accounts.get(acct_id)
something like this perhaps
BankAccount(1,20)
print BankAccount.get_account(1)
BankAccount("apple",100)
print BankAccount.get_account("apple")
this is one way to address multiple accounts, follow the other advice with respect to the penalty

Categories

Resources