So, I'm making a Account class in python. It has the basic functions of deposit, withdrawing, and checking your balance. I'm having trouble with a transfer method though.
This is my code(sorry for the code dump):
class Account:
"""simple account balance of bank"""
def __init__ (self, name, balance):
self.name = name
self.balance = balance
print('Account of ' + self.name)
def deposit(self, amount):
if amount > 0:
self.balance += amount
self.statement()
def withdrawal(self, amount):
if amount > 0 and self.balance > amount:
self.balance -= amount
self.statement()
else:
print("the ammount in your is not sufficent")
self.statement()
def statement(self):
print("Hi {} your current balance is {}".format(self.name,self.balance))
def transfer(self, amount, name):
self.balance = self.balance - amount
name.balance = name.balance + amount
return name.balance()
Now, it works for
abc = Account("abc", 0)
abc.deposit(1000)
siddharth = Account("siddharth", 159)
So how do I run following code:
siddharth.transfer(11, "abc")
siddharth.transfer(11, Account.abc)
also, how do I create account "abc" if account "abc" doesn't exist
Your code will be your best lesson about taking care of variables/parameters naming. Your method transfer(self, amount, name) should be transfer(self, amount, account). I think that now, it will be obvious that the correct code is
abc = Account("abc", 0)
abc.deposit(1000)
siddharth = Account("siddharth", 159)
siddharth.transfer(11, abc)
Be really careful on misleading names.
Aside of your question, I don't think that an Account should have a transfer method. An Account only cares about deposits and withdraws, not about what is done with them. IMO Transfer should be a function with 2 Account parameters, withdrawing from the first, making a deposit on the second. This is just to follow the Single Responsibility principle.
Following the same principle, don't put print functions in an Account. Consider that you don't know the context in which your class will be used. If it is in a web app, prints are redirected to /dev/null…
Finally, always do what you said you'll do. If I have an account with a balance b, I expect that after the call to deposit with a value v, my account balance will be b + v. No matter the value of v. You are right to check the value and not adding a negative value (that is a withdraw) so you have to warn the caller that you'll not add the value, so rise an exception. Same for withdraw.
You can first have an array of all accounts somewhere declared. Then, you can first try to find if an account exists. If not, create an account and pass them.
allAccounts = []
#create bunch of accounts including 'abc'
siddharth = Account("siddharth", 159)
searchResult = [x for x in allAccounts if x.name == 'abc']
#assuming that account names are unique
if len(searchResult) == 0:
acc = Account("abc", 11)
else:
acc = searchResult[0]
siddarth.transfer(11, acc)
Related
I am trying to use the transfer_to_saving method in the CheckingAccount class. However, whenever I create a SavingAccount object, the self.has_saving = True does not change the class attribute to True. So, whenever I try to transfer funds, it prints Must create a saving account.
class CheckingAccount(Account):
balance = 0
def __init__(self, account_number, pin):
super().__init__(account_number)
self.SavingAccount = SavingAccount
self.pin = pin
def deposit(self, amount):
old_bal = self.balance
self.balance += amount
print(f'Previous Balance: ${old_bal}\nDeposit amount: ${amount}\nNew Balance: ${self.balance}')
def withdraw(self, pin, amount):
if pin == self.pin:
self.balance -= print('Insufficient funds') if amount > self.balance else amount
else:
print('Invalid PIN')
def transfer_to_saving(self, amount):
if self.SavingAccount.has_saving is False:
print('Must create a saving account')
elif amount > self.balance:
print('Insufficient funds')
else:
self.SavingAccount.balance += amount
self.balance -= amount
class SavingAccount(Account):
balance = 0
has_saving = False
def __init__(self, account_number):
super().__init__(account_number)
self.CheckingAccount = CheckingAccount
self.has_saving = True
def deposit(self, amount):
self.balance += amount
Am I doing this right? Shouldn't the init method be changing the class attribute?
---UPDATE---
The goal I am trying to accomplish is to find out whether the user has already created a saving account. I have additional User classes that I did not include since it would be a bit overkill. However, the goal is to prevent a user from transferring money from checking to saving if they don't have a saving account.
You are not actually creating an instance of SavingAccount with this line:
self.SavingAccount = SavingAccount
You are assigning the self.SavingAccount attribute to the SavingAccount class defined below.
You need to call the SavingAccount constructor, like this:
self.saving_account = SavingAccount(account_number)
Note that the Python convention is to use lower_snake_case for attributes/variables, and UpperCamelCase for class names.
You are doing the same thing on this line in the SavingAccount constructor:
self.CheckingAccount = CheckingAccount
I'm not sure what the goal is here, but if you want every SavingAccount to hold a reference to a CheckingAccount and vice versa, it might be cleaner to do it like this:
class CheckingAccount(Account):
def __init__(self, account_number, pin):
super().__init__(account_number)
self.saving_account = SavingAccount(account_number, self)
self.pin = pin
class SavingAccount(Account):
def __init__(self, account_number, checking_account):
super().__init__(account_number)
self.checking_account = checking_account
With this, whenever you create a CheckingAccount you will get a corresponding SavingAccount and they will each hold a reference to each other. I think it's still a bit weird conceptually, since the account numbers would be the same, so it might be better to just create them separately like this:
class CheckingAccount(Account):
def __init__(self, account_number, pin):
super().__init__(account_number)
self.saving_account = None # to be assigned later
self.pin = pin
class SavingAccount(Account):
def __init__(self, account_number):
super().__init__(account_number)
self.checking_account = None # to be assigned later
checking_account_number = 123
checking = CheckingAccount(checking_account_number)
saving_account_number = 456
saving = SavingAccount(saving_account_number)
checking.saving_account = saving
saving.checking_account = checking
Finally, the has_saving attribute of SavingAccount is not necessary at all. A cleaner way to check if a SavingAccount is to use isinstance:
def transfer_to_saving(self, amount):
if not isinstance(self.saving_account, SavingAccount):
print('Must create a saving account')
elif amount > self.balance:
print('Insufficient funds')
else:
self.saving_account.balance += amount
self.balance -= amount
When you do the self.has_saving = True you are establishing an instance variable that is part of the instance. you are not modifying the class variable. to modify the class variable you would need to use the class name instead of self. reference.
I'm writing code that can generate an 16 digit account number and 4 digit password.
The account can store a balance which I'm keeping track of via a class (hopefully using the 16 digit number as the instance of said class)
My code bellow shows where I'm at
card_pin_dict = {}
class Account():
def __init__(self,number,pin):
self.number = number
self.pin = pin
def balance(self):
balance = 0
return balance
def gen_account_num():
global new_card_num
global randpin
new_card_num = "40000" + str(random.randint(1000000000, 9999999999)) #creates account number
randpin = str(random.randint(1000, 9999))
card_pin_dict[new_card_num] = randpin
print(card_pin_dict) #This is to check the password against account number later
print(f"Your card number:\n{new_card_num}\nYour card PIN:\n{randpin}")
which works fine.
What I want to do...
def gen_account_num():
#bottom of function
new_card_num = Account(new_card_num,randpin) #using the number as the instance
def print_balance()
id = input("What is your account number?) #4000001029458302 (example)
print(id.balance)
Make your first gen_account_num a class method.
card_pin_dict = {}
class Account:
def __init__(self, number, pin):
self.number = number
self.pin = pin
self._balance = 0
#property
def balance(self):
return balance
#classmethod
def gen_account(cls):
new_card_num = "40000" + str(random.randint(1000000000, 9999999999)) #creates account number
randpin = str(random.randint(1000, 9999))
card_pin_dict[new_card_num] = randpin
return cls(new_card_num, randpin)
a = Account.gen_account()
__init__ is usually a "dumb" method, the dumber the better. Class methods are a good way to encapsulate code needed to generate the arguments to the dumb initializer.
We make balance a property, rather than a public attribute of the class, to limit the ways you can modify the balance. Above, we make it a read-only property by not defining a setter. Only methods you provide (like deposit or withdraw) should modify the value of the _balance attribute.
For print_balance, you first need something that maps account numbers to instances of Account. This could be a simple dict, similar to the one you use to map card numbers to pins. Then print_balance looks something like
def print_balance():
id = input("...")
acct = account_dict[id]
print(acct.balance)
I have a class that looks like this:
class Account(object):
"""A simple bank account"""
def __init__(self, balance=0.0):
"""
Return an account object with a starting balance of *balance*.
"""
self.balance = balance
def withdraw(self, amount):
"""
Return the balance remaining after withdrawing *amount* dollars.
"""
self.balance -= amount
return self.balance
def deposit(self, amount):
"""
Return the amount remaining after depositing *amount* dollars.
"""
self.balance += amount
return self.balance
I'll initialize it in xyz:
xyz = Account(balance=6000)
xyz.balance
> 6000
I also have a dumb printing function:
def thing():
print("I am doing a thing...")
When I try to call the deposit method in my schedule flow:
import schedule
# this works
# schedule.every(5).seconds.do(thing)
# this doesn't work
schedule.every(5).seconds.do(xyz.deposit(2300))
while True:
schedule.run_pending()
I get the following error:
TypeError: the first argument must be callable
Any ideas? Is it even possible to call methods within a schedule flow?
Not familiar with schedule, but it seems like do() wants a callable, i.e. a method. You're giving it the return value of xyz.deposit(2300), rather than the method xyz.deposit and the argument 2300. Try this:
schedule.every(5).seconds.do(xyz.deposit, 2300)
I have been learning Python as my first language for about two weeks now and I love it. I have been using Learn Python the Hard way, but when I hit Object Oriented Programming my brain just about exploded. I did many hours of research and I thought I finally got the gist, but now I am a little stuck.
I have created a very simple banking program, attempting to use Classes. I was doing fine, until I hit a big issue. As it is, it works (I have not posted the menu setup for brevity, but it does what I want as long as I only have these three objects.) There in lies the problem.
Issue: How do I manipulate instance values if there are multiple instances. TLDR: How can I not hard code object references?
Please see the Transfer function in my main BankAccount Class: I hardcoded in the objects(accounts) saccount.balance and paccount.balance variables, but what if there were many different accounts? How would I be able to edit their balances aka do transfers?
How can I make the Transfer() method correctly reference the instances they need to go to? Am I asking this right? Am I using OOP incorrectly?
What if there were multiple users or multiple bank accounts? like "daccount", "faccount" etc how would I manage their balances and transfers?
Please be gentle...
Here is my main Class:
class BankAccount:
#### NO CLASS VARIABLES
def __init__(self):
self.balance = 500 #This is an instance variable
def withdraw(self, amount):
self.balance = self.balance - amount
print "You withdrew %d dollars\n" % amount
return self.balance
def deposit(self, amount):
self.balance += amount
print "You deposited %d dollars\n" % amount
return self.balance
def transfer(self, amount): ### Here is our problem
print "Where would you like to transfer money from?"
answer = raw_input("1.) From CHECKINGS to SAVINGS\n2.)From SAVINGS to CHECKINGS\n >>>")
if answer == "1":
baccount.balance -= amount #What would I do if there were many accounts?
saccount.balance += amount #I originally tried this as SavingsAccount.balance, but that would be a "Class Variable" correct?
elif answer == "2":
saccount.balance -= amount
baccount.balance += amount
else:
menu()**
def printbal(self):
print "Your balance is currently %d dollars." % self.balance
return self.balance
Here are my two subclasses (A minimum balance checkings, and a savings)
class MinBalAccount(BankAccount): #This is a BankAccount and has a MinBal
def __init__(self, minbalance): #This sets our minbalance during 'instantation'
BankAccount.__init__(self) #This gives us the variable self.balance
print "ATM INITIALIZED. WELCOME TO SKYNET BANK"
self.minbalance = minbalance #Sets the minimum balance for this minbalaccount
def withdraw(self, amount):
while self.balance - amount < self.minbalance: #THis allows for a loop,
print "Apologies, you must maintain a balance of 1.00"
print "If you withdraw %d from your current balance of %d it will leave you with a balance of %d dollars." % (amount, self.balance, self.balance - amount)
print "Please Re-Enter The AMOUNT You would like to withdraw"
amount = int(raw_input("\nAmount:"))
BankAccount.withdraw(self, amount)
self.printbal() #We can do this because we inherited it from out parent class. We could also write it as MinBalAccount.printbal(self) or BankAccount.printbal(self)
class SavingsAccount(BankAccount):
def __init__(self,minbalance,balance):
self.minbalance = minbalance
self.balance = balance
paccount = BankAccount()
baccount = MinBalAccount(1.00)
saccount = SavingsAccount(300, 500)
How can I make the Transfer() method correctly reference the instances they need to go to? Am I asking this right?
Am I using OOP incorrectly?
You declare object references in Python the same way you do any other variable, you run the constructor of the class and assign it to a value. If you want to transfer from one account and into another account (regardless of the account), you want to pass the object references as arguments to the function in the method (assuming these accounts are separate from each other).
Consider the design of your BankAccount class: You're currently using your transfer method to transfer from two fixed accounts. If you want to transfer from the current BankAccount object (IE "self"), to another account (whichever one is passed to the method), you would write your method like so:
def transferTo(self, otherAccount, amount):
if (self.balance >= amount):
self.balance -= amount
otherAccount.balance += amount
Then when you call this method, you simply indicate which account to transfer the funds to.
baccount.transferTo(saccount, 100)
And you're done! I would recommend keeping the IO operations (such as asking the user for input) outside of these methods since you could want to perform transfers that don't need user input.
You can treat object references the same way you could any other value. Therefore you can pass them to any method, place them in a list, etc.
What if there were multiple users or multiple bank accounts? like "daccount", "faccount" etc how would I manage their balances and transfers?
You should separate the concept of an AccountHolder from each BankAccount. An AccountHolder may have multiple BankAccounts, and each BankAccount is then provided it's own balances, number, etc. You can assign class instances to instance variables in the method of a class. The AccountHolder class should have a list of BankAccount objects, and provide some basic methods that return certain key accounts (such as a checkingAccount() method). A constructor like this would work well for a User:
class AccountHolder:
def __init__(self, name):
self.user_name = name
self.checking = BankAccount()
self.savings = MinBalAccount()
I believe, however, that the key to your question is to pass the object references as arguments to methods, allowing you to more generically treat each instance of a BankAccount. Understandably this is your first real encounter with OOP, so it's certain to be overwhelming. I wish you luck!
You have to modify your transfer function. It needs 1) the amount 2) the destination account
def transfer(self, amount, destination): ### Here is our problem
self.balance -= amount
destination.balance += amount
And add the following code at the end
print "Where would you like to transfer money from?"
answer = raw_input("1.) From CHECKINGS to SAVINGS\n2.)From SAVINGS to CHECKINGS\n >>>")
amount = int(raw_input("Amount to transfer ? "))
if answer == "1":
baccount.transfer(amount, saccount)
elif answer == "2":
saccount.transfer(amount, baccount)
else:
menu()**
IMO you're running into an issue here because transfer() isn't really a good interface for a BankAccount instance. withdraw() and deposit() make sense on their own, but transfer() would at least require passing another argument, rather than hard-coding a global variable (in general i try to avoid using global variables).
What I would do instead is to have another class e.g. User which owns the paccount, baccount, saccount variables, in addition to the transfer(), deposit(), and withdraw() methods which guide you through the menu e.g.:
class User:
def __init__(self):
self._paccount = BankAccount()
self._baccount = MinBalAccount(1)
self._saccount = SavingsAccount(300, 500)
def deposit(self, amount):
num = input('Where do you want to deposit your money? 1) Savings 2) MinBal 3) Checking').strip()
if num == '1':
self._saccount.deposit(amount)
...
def transfer(self, amount):
print('Where would you like to transfer money from?')
...
user = User()
user.deposit(200)
user.transfer(500)
I've looked at similar questions and still have not been able to figure this out. I'm absolutely sure i'm making a very stupid mistake somewhere but I just can't seem to find it.
For this code.
class BankAccount:
def __init__(self, initial_balance):
self.balance = initial_balance
def deposit(self, amount):
self.deposit = amount
self.balance = self.balance + self.deposit
def withdraw(self, amount):
self.withdraw = amount
self.balance = self.balance - self.withdraw
self.fee = 5
self.total_fees = 0
if self.balance < 0:
self.balance = self.balance - self.fee
self.total_fees += self.fee
def get_balance(self):
current_balance = self.balance
return current_balance
def get_fees(self):
return self.total_fees
When I run the code everything works fine when I run this
my_account = BankAccount(10)
my_account.withdraw(15)
my_account.deposit(20)
print my_account.get_balance(), my_account.get_fees()
However, if I make an additional call to withdraw
my_account = BankAccount(10)
my_account.withdraw(15)
my_account.withdraw(15)
my_account.deposit(20)
print my_account.get_balance(), my_account.get_fees()
It throws this error.
TypeError: 'int' object is not callable
I don't understand why it works fine until I make an additional call to withdraw. Please help.
When you do self.deposit = amount, you overwrite your deposit method with the amount. You do the same in withdraw with self.withdraw = amount. You need to give the data attributes a different name from the methods (like call the method withdraw but the attribute withdrawalAmount or something like that).
When you do this inside the withdraw method
self.withdraw = amount
you replace the it with whatever amount is. Next time you call withdraw, you get the amount object. Which in your case is an int.
The same applies to deposit:
self.deposit = amount
Give your data members names that are different to your methods.