Python Class Inheritance: Recursion Error - python

I'm trying to wrap my head around classes and I don't see what I have done wrong in this script. I have built a few nested classes.
import random
class Account(object):
num_accounts = 0
def __init__(self, name, balance):
self.name = name
self.balance = balance
Account.num_accounts += 1
def withdraw(self, amt):
self.balance = self.balance - amt
def inquiry(self):
return self.balance
class EvilAccount(Account):
def __init__(self,name,balance,evilfactor):
Account.__init__(self,name,balance)
self.evilfactor = evilfactor
def inquiry(self):
if random.randint(0,4) == 1:
return self.balance * self.evilfactor
else:
return self.balance
class MoreEvilAccount(EvilAccount):
def deposit(self,amount):
self.withdraw(5.00)
EvilAccount.deposit(self,amount)
class WithdrawCharge(object):
fee = 2.50
def withdraw_fee(self):
self.withdraw(self.fee)
class MostEvilAccount(EvilAccount,
WithdrawCharge):
def withdraw(self,amt):
self.withdraw_fee()
super(MostEvilAccount,self).withdraw(amt)
And then I instantiate it all with
d = MostEvilAccount("Dave", 500.00, 1)
And everything is fine. But when I try to call an inherited method:
d.withdraw(5.00)
I get recursion errors!
File "StackO.py", line 37, in withdraw
self.withdraw_fee()
File "StackO.py", line 33, in withdraw_fee
self.withdraw(self.fee)
File "StackO.py", line 37, in withdraw
self.withdraw_fee()
RecursionError: maximum recursion depth exceeded
This is right out of Python Essential Reference by David M. Beazley, p121.
Why am I getting Recursion Errors?

Your issue is that WithdrawCharge doesn't actually have a method called withdraw on it. So, when you call it from withdraw on MostEvilAccount, Python has to search for an appropriate method, which is withdraw on MostEvilAccount as the self you passed into WithdrawCharge.withdraw_fee is a MostEvilAccount. To illustrate:
d = MostEvilAccount("Dave", 500.00, 1)
d.withdraw(5.00)
--> In MostEvilAccount.withdraw at line 37
----> In WithdrawCharge.withdraw_fee at line 33
------> In MostEvilAccount.withdraw at line 37
--------> In WithdrawCharge.withdraw_fee at line 33
and it continues...
There are a few ways you could go about fixing this. You could take the functionality of WithdrawCharge and embed it in MostEvilAccount.withdraw. You could also pass Account.withdraw in to withdraw_fee as an argument and have it call that.

This error is here to prevent stack overflows. More info about how it works here

Related

AttributeError: class object has no attribute

I am new to python. I try to access the attribute acnt_amount from the class bank_Customer, but throws "AttributeError" error. How to access the attribute of the function getDetails to withdraw with in the class from one function to another function? What is the mistake that i do? Any help will be much appreciated! Thanks in advance!
Code:
class bank_Customer:
def getDetails(self, cname, acnt_no, acnt_type, acnt_amount):
self.cname = cname
self.acnt_no = acnt_no
self.acnt_type = acnt_type
self.acnt_amount = acnt_amount
row = self.cname + "," + str(self.acnt_no) + "," + self.acnt_type + "," + str(self.acnt_amount) + "\n"
file = open('cust_details.csv','a')
file.write(str(row))
file.close()
print('*'*40)
print("Account has been added successfully!")
return self.acnt_amount
def withdraw(self):
cash = int(input("Please enter the amount to be withdrawn: "))
self.acnt_amount = self.acnt_amount - cash
f"balance amount is {balance}"
return balance
base = bank_Customer()
base.withdraw()
Error:
Traceback (most recent call last):
File "C:\Users\kisha\IdeaProjects\Git projects in python\ATM application.py", line 96, in <module>
base.withdraw()
File "C:\Users\kisha\IdeaProjects\Git projects in python\ATM application.py", line 66, in withdraw
self.acnt_amount = self.acnt_amount - cash
AttributeError: 'bank_Customer' object has no attribute 'acnt_amount'
As suggested, an init is required. Also consider setting up some defaults, and look at the use of "getters and setters".
You may gain some insight from the following sample bank account class that I wrote some time ago as an example.
# #Author:srattigan
# #Date:2020-12-10 11:10:33
# #LastModifiedBy:srattigan
# #Last Modified time:2020-12-14 09:50:13
# demo class for inheritance
class BankAccount:
"""Generic Bank Account
"""
acc_num = 100000
def __init__(self, name, balance=0):
self.name = name
self.balance = balance
self.account_num = self.acc_num
BankAccount.acc_num += 1
def deposit(self, amount):
assert amount > 0, ValueError("Must deposit a positive amount")
self.balance += amount
def withdraw(self, amount):
self.balance -= amount
def __str__(self):
rep = f"Bankaccount for {self.name}"
rep += f"\n\tAcc Num: {self.account_num}"
rep += f"\n\tBalance: €{self.balance:.2f}"
return rep
customer = BankAccount("Fred Jones", 99)
print(customer)
You need to def init as your first method. Thats where you specify cname, acnt_no etc.
you need to declare the variables inside the class first.
create the init() constructor to declare and initialize those variables

Python code doesn't show date-time and also doesn't throw any errors

I've been learning python from an online course and in the oop section I made a simple bank account class with simple methods like deposit and withdrawal and the instructor also showed the use of datetime function from pytz and datetime. My static method in class doesn't throw an error except it gives me this <function "Class-Name"._time at 0x00000251A4D72790> instead of something like this
2021-07-30 21:40:47.669274+00:00 and the .astimezone throws this attribute error AttributeError: 'function' object has no attribute 'astimezone'
Meanwhile I also downloaded the instructors code and couldn't find any major difference in our code and the instructors code ran without any issues.
[import datetime
import pytz
class Account:
#staticmethod
def _time():
date = datetime.datetime.utcnow()
return pytz.utc.localize(date)
def __init__(self, name, balance):
self.name = name
self.balance = balance
self.trans_list = \[\]
def print_balance(self):
print("Current balance is {}".format(self.balance))
def deposit(self, amount):
if amount > 0:
self.balance += amount
self.print_balance()
self.trans_list.append((Account._time, amount))
def withdraw(self, amount):
if 0 < amount <= self.balance:
self.balance -= amount
self.print_balance()
self.trans_list.append((Account._time, -amount))
def transc_period(self):
for date_times, amount in self.trans_list:
if amount > 0:
tran_type = "deposited"
else:
tran_type = "withdrawn"
amount *= -1
print("{:6} {} on {} )".format(amount, tran_type, date_times))
if __name__ == '__main__':
account = Account("default", 0)
account.deposit(1000)
account.transc_period()
account.withdraw(500)
account.transc_period()][1]
The astimezone line comes in transc_period last line
[print("{:6} {} on {} )".format(amount, tran_type, date,date.astimezone()))][1]
I have corrected your code below. The key points were:
For the issue: <function "Class-Name"._time at 0x00000251A4D72790> you were referring to the function definition, but not actually invoking the function. So, you should call date_times() instead of date_times.
For the issue AttributeError: 'function' object has no attribute 'astimezone', the issue is related. Because you did not call the function, you could not call the underlying methods to the object the function generates.
Apart from that I removed a few parts of the code that seemed to be misplaced. (self.trans_list = [] --> self.trans_list = []) and (account.transc_period()][1] --> account.transc_period())
import datetime
import pytz
class Account:
#staticmethod
def _time():
date = datetime.datetime.utcnow()
return pytz.utc.localize(date)
def __init__(self, name, balance):
self.name = name
self.balance = balance
self.trans_list = []
def print_balance(self):
print("Current balance is {}".format(self.balance))
def deposit(self, amount):
if amount > 0:
self.balance += amount
self.print_balance()
self.trans_list.append((Account._time, amount))
def withdraw(self, amount):
if 0 < amount <= self.balance:
self.balance -= amount
self.print_balance()
self.trans_list.append((Account._time, -amount))
def transc_period(self):
for date_times, amount in self.trans_list:
if amount > 0:
tran_type = "deposited"
else:
tran_type = "withdrawn"
amount *= -1
print("{:6} {} on {}".format(amount, tran_type, date_times().astimezone()))
if __name__ == '__main__':
account = Account("default", 0)
account.deposit(1000)
account.transc_period()
account.withdraw(500)
account.transc_period()

4 Classes, 1 performs actions and asks for input, the others keep track of values

I'm working on a project for CS1410, where I need to simulate a Coffee Machine. I have quite a bit so far, but I'm a bit stuck, specifically with these questions:
In the class CashBox, under the function deposit, I'm trying to add the values together from any coins inserted in the oneAction function from the CoffeeMachine class. However I get the error "'CoffeeMachine' object has no attribute 'credit'" and I'm struggling with understanding why. I can tell it's an inheritance problem, but I'm unsure exactly how to fix it.
How should I format it so that when I get the input in oneAction(), I can take that input and mess with it in the cashBox? What am I doing wrong with self.something, and how can I recognize when to use self, and when to just use a normal variable
I wasn't sure how to upload the code so that the problem is reproducible without giving at least this file, it's all pretty tied together. Some advice on how I could have presented this better would be helpful as well.
Past the original question, any further advice would be seriously appreciated.
Seriously.
Thank you all, hopefully the code is pretty readable.
class CashBox(object):
def __init__(self):
self.credit = 0
self.totalReceived = 0.0
def deposit(self,amount):
self.credit = amount + self.credit
self.totalReceived = amount + self.totalReceived
print(self.totalReceived,self.credit)
def returnCoins(self):
print("Returning ", self.totalReceived, " cents.")
self.totalReceived = 0.0
def haveYou(self,amount):
return self.credit >= amount
def deduct(self,amount):
pass
def totalCoins(self):
return self.totalReceived
class CoffeeMachine(object):
def __init__(self):
self.cashBox = CashBox()
self.selector = self.cashBox
def oneAction(self):
while True:
command = input("""
______________________________________________________
PRODUCT LIST: all 35 cents, except bouillon (25 cents)
1=black, 2=white, 3=sweet, 4=sweet & white, 5=bouillon
Sample Commands: insert 25, select 1. Your command:
""")
words = command.lower().split()
if 'select' in words:
Selector.select(self,int(words[1]))
print("Great selection!")
elif 'insert' in words:
coinsAllowed = [5,10,25,50]
if int(words[1]) in coinsAllowed:
CashBox.deposit(self,int(words[1]))
else:
print("""
That is not one of the allowed coins,
please insert a penny, nickel, dime, quarter,
or half-dollar. Thank you.
""")
elif 'cancel' in words:
print("Cancelling transaction. Returning to main menu: ")
CashBox.returnCoins(self)
elif 'quit' in words:
print("Have a nice day!")
else:
print("That is not an option")
def totalCash(self):
pass
class Product(object):
def __init__(self,name,price,recipe):
self.name = name
self.price = price
self.recipe = recipe
def getPrice(self):
return self.price
def make(self):
print(self.recipe)
class Selector(object):
def __init__(self):
self.cashBox = CashBox
self.products = []
#self.products.append(Product.
def select(self, choiceIndex):
pass
def main():
m = CoffeeMachine()
while m.oneAction():
pass
#total = m.totalCash()
#print(f"Total Cash: ${total/100:.2f}")
if __name__ == "__main__":
main()
Exception has occurred: AttributeError
'CoffeeMachine' object has no attribute 'credit'
File "C:\Users\Tanner Harmer\Desktop\Coffee2\CashBox.py", line 7, in deposit
self.credit = amount + self.credit
File "C:\Users\Tanner Harmer\Desktop\Coffee2\CashBox.py", line 46, in oneAction
CashBox.deposit(self,int(words[1]))
File "C:\Users\Tanner Harmer\Desktop\Coffee2\CashBox.py", line 89, in main
while m.oneAction():
File "C:\Users\Tanner Harmer\Desktop\Coffee2\CashBox.py", line 95, in <module>
main()
You're calling the CashBox by using the class name, like if the method were static, but you created an instance of this class (in the constructor self.cashBox = CashBox()) so use it
CashBox.deposit(self,int(words[1])) // OLD, NO
self.cashBox.deposit(self,int(words[1])) // YES
use the cashBox of the CoffeeMachine

OOP: How to write pythonic interaction between classes?

I would like to model the interaction between two classes where one of the classes takes the other one as an argument in one of its methods. Which class should be in the argument of the method of the other class?
I have written two alternative solutions to the problem but I am not sure which one of them is considered to be the correct way of handling this issue. Maybe there is even a better way but here are my two alternatives:
class BankAccount:
def __init__(self, balance):
self._balance = balance
def transaction(self, cash):
self._balance += cash._value
cash._value = 0
class Cash:
def __init__(self, value):
self._value = value
def transfer(self, bank_account):
bank_account._balance += self._value
self._value = 0
if __name__ == "__main__":
# First alternative
acc = BankAccount(balance=100)
cash = Cash(value=10)
print('-' * 30)
print('First alternative')
print(f'Account balance before: {acc._balance}')
print(f'Cash value before: {cash._value}')
acc.transaction(cash=cash)
print(f'Account balance after: {acc._balance}')
print(f'Cash value after: {cash._value}')
# Second alternative
acc = BankAccount(balance=100)
cash = Cash(value=10)
print('-' * 30)
print('Second alternative')
print(f'Account balance before: {acc._balance}')
print(f'Cash value before: {cash._value}')
cash.transfer(bank_account=acc)
print(f'Account balance after: {acc._balance}')
print(f'Cash value after: {cash._value}')
As you can see, both alternatives show the same results but I'd be happy to get a recommendation for the pythonic way of modelling this kind of class interaction. Thanks.
The example is ill-formed which prevents us from concentrating on actual OOP. It does not make sense for some cash to lose its value because it is tranfered. Does a $20 bill lose its value because you deposit it at the bank?
A new example
Instead let's consider the problem of representing money transfer between two bank accounts.
A key concept of OOP is that attributes of an instance should not be directly updated. Instead, the instance should provide an API via methods which provide some control over its state.
We can accomplish that by defining the methods deposit and withdraw for a BankAccount. This way, we can then define the method transfer_to which only makes use of this API.
class BankAccount:
def __init__(self):
self.balance = 0
def deposit(self, amount):
self.balance += amount
def withdraw(self, amount):
if amount >= self.balance:
self.balance -= amount
else:
raise ValueError('Insufficient funds')
def transfer_to(self, target, amount):
if isinstance(target, BankAccount):
self.withdraw(amount)
target.deposit(amount)
else:
raise ValueError("'target' must be another BankAccount")
Advantage
By encapsulating the logic of withdraw, deposit and tranfer_to, we allow for simple implementation of more complex types of accounts through inheritance.
Here let's give the example of a new type of BankAccount which allows for negative balance.
class CreditAccount(BankAccount):
def withdraw(self, amount):
self._balance -= amount
By fully delegating the responsability of withdrawing to instances of BankAccount, we allow external agents to manipulate an account without having to be aware of the inner logic of withdrawing and depositing.

How to add custom parameter parameter in python functions?

My name is Piyush and I have just started learning to code in python. Right now I am working on a project. And would really appreciate if anyone can help in adding customized parameters in functions.
I am mentioning my code and the problem I am facing down below:
class Chips ():
def __init__ (self, total):
while True:
try:
total = int(input("How many chips as total?: ?"))
except:
print("Please enter an integer")
else:
break
self.total = total
self.bet = 0
def win_bet (self):
self.total = self.total + self.bet
def loose_bet (self):
self.total = self.total - self.bet
However, I can set total = 100 and can run the game but I want the user to be able to enter the total chips he/she wants to add.
I want the input(total) in the while loop to be as the argument while running the game. But I keep on getting this error:
--
TypeError Traceback (most recent call last)
<ipython-input-9-b1b7b1c195f7> in <module>()
367
368 # Set up the Player's chips
--> 369 player_chips = Chips() # remember the default value is 100
370
371 # Prompt the Player for their bet:
TypeError: __init__() missing 1 required positional argument: 'total'
Please Help!
Thanks for your attention to my request.
Your class takes a parameter in its constructor, but you also read it from the input in your constructor.
I think you are confused in what you are trying to achieve here.
Option 1:
If the caller of your code (the code that constructs your class), can be modified and know the total at the instance creation time, just add the parameter in the constructor call.
total = 100
player_chips = Chips(total)
Option 2:
in case you can't modify the caller, (most likely from what I read), then that means you want to actually read the total from the input. Remove the argument from your constructor.
def __init__ (self):
instead of
def __init__(self, total):
You should add method get_total_from_user and set default param in constructor
class Chips ():
def __init__ (self, total=None):
self.total = self.get_total_from_user() if not total else total
self.bet = 0
def win_bet (self):
self.total = self.total + self.bet
def loose_bet (self):
self.total = self.total - self.bet
def get_total_from_user(self):
while True:
try:
return int(input("How many chips as total?: ?"))
except:
print("Please enter an integer")
else:
break
It allows you to get total from user
Chips()
Or you can set it via passing value
Chips(100)
c = Chips(100)
at the bottom of your code - no error. You override the value of total in the interactive constructor, but hey, you promised "init" you were sending it.
c = Chips()
works if you change your signature to:
def __init__ (self, total):
The interactive constructor seems like a very bad idea overall.

Categories