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.
Related
I'm in doubt with this program of mine with inheritance, I don't know if I understand the concept wrong or the code is wrong (maybe both), but I really need some help.
The other functions are working, the only problem is when I try to access the Saving class function by the Account (Subclass).
class Savings:
def __init__(self):
self.money = 0
self.statement = []
def apply(self, value):
self.money += value
self.statement.append(("Apply", f"${value}"))
class Accounts(Savings):
def __init__(self, client: Client, bank: Bank):
super().__init__()
#other variables
def change_money(self):
print("3 - Apply in Savings")
choose = int(input("Choose: "))
elif choose == 3:
value = float(input("Value to apply: $").replace(",", "."))
super().apply(value)
print(super().money)
else:
pass
And when I try to access the money variable, it says
super().money
AttributeError: 'super' object has no attribute 'money'
I made a test using only Accounts as Object and the money variable changed,
Input:
a = Accounts()
a.change_money()
a.money
Output
3 - Apply in Savings
Choose: 3
Value to apply: $100
100.0
but Accounts and Savings are different classes and I need to access it and change from the Subclass
Please, can anyone help me ?
You can use self.apply(value) instead:
class Savings:
def __init__(self):
self.money = 0
self.statement = []
def apply(self, value):
self.money += value
self.statement.append(("Apply", f"${value}"))
class Accounts(Savings):
def change_money(self):
value = float(input("Value to apply: $"))
self.apply(value)
print(self.money)
a = Accounts()
a.change_money() # input, say, 10
print(a.statement) # [('Apply', '$10.0')]
Your object a inherits the method apply attached to itself, so a can call its own method by self.apply.
You don’t need to call super as it is a pre defined function and is part of the savings accounts class. Just call self.apply(value)
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 am just confused about why this is happening. When I run my assert it seems that each test is not creating its own object. so when i get to the last assert statement the test fails because the other assert are still in the list. Any help would be great thank you
My Class code:
from datetime import date
class Transaction():
"""
Transaction
"""
balance = 0.0
timestamp = date.today()
def __init__(self, amount, dt=None):
self.balance = amount
self.transactions = []
if dt is None:
self.timestamp = date.today()
else:
self.timestamp = dt
def __repr__(self):
return '{self.__class__.__name__}({self.balance:,.2f}, {self.timestamp})'.format(self=self)
def __str__(self):
return f'{self.timestamp}: ${self.balance:,.2f}'
class Account():
"""
Account Class
"""
balance = 0.0
transaction = Transaction(balance)
def __init__(self):
self.balance = 0.0
def deposit(self, amount):
self.balance += +amount
self.transaction.transactions.append(+amount)
def withdraw(self, amount):
self.balance -= amount
self.transaction.transactions.append(-amount)
def get_balance(self):
if len(self.transaction.transactions) < 1:
return 0
return sum(self.transaction.transactions)
My Code for pytest:
def test_append_transaction():
account = Account()
account.deposit(200)
assert account.transaction.transactions == [200]
def test_deposit():
user = Account()
user.deposit(300)
assert user.balance == +300.00
def test_append_withdraw():
account = Account()
account.withdraw(50)
assert account.transaction.transactions == [-50]
def test_withdraw():
account = Account()
account.withdraw(50)
assert account.balance == -50.0
Your tests are failing because your code is wrong - i.e. they are failing because you've written your tests correctly, in a way which is able to detect bugs, and they detected a bug in your code.
Yes, each test function does create a new Account instance, as you can clearly see in the test functions themselves. However, each Account instance does not have its own distinct Transaction instance, because you made this a class attribute instead of an instance attribute.
To fix the bug in your Account class, you should initialise
self.transaction = Transaction(self.balance)
in the __init__ method, so that each Account instance has a reference to a distinct Transaction instance.
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)
I am new to unittesting in python and this is my first unit test. I don't know whether I am doing right or wrong unit test , need some help. I have to test function, in first function i want to test legal deposit and in second function I want to test illegal deposit, like depositing "apple" or "lizard" instead of amount . Since I am new to unit test, I have lot of confusion about it. I read different post,but in my case I am still felling difficult to write unit test for this two functions.
bankaccount.py
class BankAccount():
def __init__(self):
self.account_number=0
self.pin_number=""
self.balance=0.0
self.interest=0.0
self.transaction_list=[]
def deposit_funds(self, amount):
self.balance+=amount
def withdraw_funds(self, amount):
if amount<=balance:
self.balance-=amount
import unittest
from bankaccount import BankAccount
class TestBankAcount(unittest.TestCase):
def setUp(self):
# Create a test BankAccount object
self.account = BankAccount()
# Provide it with some property values
self.account.balance = 1000.0
def test_legal_deposit_works(self):
# code here to test that depsositing money using the account's
# 'deposit_funds' function adds the amount to the balance.
self.assertTrue(100,self.account.deposit_funds(100))
def test_illegal_deposit_raises_exception(self):
# code here to test that depositing an illegal value (like 'bananas'
# or such - something which is NOT a float) results in an exception being
# raised.
unittest.main()
You could do something like this:
Have your class raise an error when the type of values provided to deposit_funds does not match the use case.
class BankAccount:
def __init__(self):
self.account_number = 0
self.pin_number = ""
self.balance = 0.0
self.interest = 0.0
self.transaction_list = []
def deposit_funds(self, amount):
try:
self.balance += amount
except TypeError:
raise TypeError
def withdraw_funds(self, amount):
if amount <= balance:
self.balance -= amount
Have your tests detect that a TypeError is thrown when that happens.
class TestBankAcount(unittest.TestCase):
def setUp(self):
self.test_account = BankAccount()
self.test_account.balance = 1000.0
def test_legal_deposit(self):
expected_balance = 1100.0
self.test_account.deposit_funds(100.0)
self.assertEqual(expected_balance, self.test_account.balance)
def test_illegal_deposit_raises_exception(self):
# code here to test that depositing an illegal value (like 'bananas'
# or such - something which is NOT a float) results in an exception being
# raised.
with self.assertRaises(TypeError):
self.test_account.deposit_funds('dummy value')
if __name__ == '__main__':
unittest.main()