Code is looking for attributes in the wrong class - python

This code's really unfinished, but it's getting there. I'm trying to organize the inputs of an item's name and price to a list, but I'm getting an error caused by the code looking for the _purchases attribute in the Item class instead of the Receipt class. What's causing this?
import datetime
class Item:
def __init__(self,_name="None",_price=0,_taxable="no"):
self._name=_name
self._price=_price
self._taxable=_taxable
def __str__(self):
base="{:-<20}".format(self._name)+"{:->20}".format(self._price)
return base
def getPric(self):
pass
def getTax(self):
pass
class Receipt:
def __init__(self,_tax_rate=0,_purchases=""):
self._tax_rate=_tax_rate
self._purchases=_purchases
def __str__(self):
pass
def additem(self):
list=self._purchases.append(self)
#Main Program
if __name__=="__main__":
loop="no"
print("Welcome to Receipt Creator")
while True:
name=input("Enter Item name: ")
price=float(input("Enter Item Price: "))
taxable=input("Is the item taxable (yes/no): ")
product=Item(name,price,taxable)
print(product)
print(Receipt.additem(product))
print(list)
loop=input("Add another item (yes/no): ")
if loop=="yes":
continue
else:
break
print("----- Receipt",str(datetime.datetime.now()),"-----")
print(list)
EDIT: Here's the error
Traceback (most recent call last):
File "C:\Users\lucas\Desktop\main.py", line 35, in <module>
print(Receipt.additem(product))
File "C:\Users\lucas\Desktop\main.py", line 23, in additem
list=self._purchases.append(self)
AttributeError: 'Item' object has no attribute '_purchases'

You have a clear idea of what you have, but the execution has a lot of room for improvement.
Let's not touch the Item class, since that is not the one that is causing troubles. But as regards to Receipt, let's create a list for purchases to hold the purchases, and let's define a method (add_item) to populate that list:
class Receipt:
def __init__(self, tax_rate=0):
self.tax_rate = tax_rate
self.purchases = []
def add_item(self, item):
self.purchases.append(item)
Now, you definitely need to instantiate that Receipt class (as opposite as you were doing), so in your main loop you should have something like:
if __name__=="__main__":
print("Welcome to Receipt Creator")
rcpt = Receipt()
while True:
name = input("Enter Item name: ")
price = float(input("Enter Item Price: "))
taxable = input("Is the item taxable (yes/no): ")
product = Item(name, price, taxable)
print(product)
rcpt.add_item(product)
print(rcpt.purchases)
loop = input("Add another item (yes/no): ")
if loop == "yes":
continue
else:
break
print(rcpt.purchases)
Now, some things to note in your previous code:
Unless you want to let other people know that an attribute is meant to be used only inside the definition of a class, there's no actual need to use a leading underscore to name an attribute.
You were trying to print list. Keep in mind that list is a builtin class, so try name your attributes using something different (you can use a trailing underscore, actually. Like list_). Moreover, you were trying to print the list attribute that you had defined in your additem() method, without having instantiated the Receipt class and ask the class for it (with something like instance.list_).
In your __init__ method of the Receipt class that you had defined, you had a default value for _purchases to be "" (an empty string), but that attribute was intended to be a list. (since you were trying to use the append() method after in your additem() method), which makes no sense at all.

#revliscano has an answer that fixes your issues. I will leave the answer to him. I am just going to explain why you got the somewhat confusing error that you got.
looking for _purchases attribute in the Item class
In your class Receipt you have:
class Receipt:
...
def additem(self):
list=self._purchases.append(self)
and were later calling it with:
product=Item(name,price,taxable)
print(product)
print(Receipt.additem(product))
So you are calling additem() on the Receipt class, not on an instance of that class. Normally when you call an instance method, the instance you are calling it with is passed in as the self by python. It did not have an instance since you called it on the class itself, and as a result under the hood python was passing the product in as the self variable because python was treating it like a call on a static class method though it was supposed to be an instance method (i.e. no cls or self arg, and just pass the given arg to the method). So because you called it like Receipt.additem(product), the product was passed as the self.
That meant that when it tried to do the self._purchases.append(self) it was like it was trying to do product._purchases.append(product) and so it was trying to find the _purchases attribute on product. Since product is an instance of the Item class, that is how you get your confusing error message.

Related

Python,unable to call initial arguments outside the class

The below python class have empty dictionary as initial arguments, after calling
createAccount() outside class it successfully add data to dictionary but I can't access dictionary outside class.
What changes shall I make in the below code to access the newly created account details ?
*Please note that my error occurs in the last line of the code *
class SavingsAccount():
def __init__(self):
self.savingsAccounts = {}
def createAccount(self, name, initialDeposit):
print()
self.accountNumber = int(12345)
self.savingsAccounts[self.accountNumber] = [name, initialDeposit]
print("Account creation has been successful. Your account number is ", self.accountNumber)
SavingsAccount().createAccount(name = 'a',initialDeposit=4)
print(SavingsAccount().savingsAccounts[12345]) # getting error here
You should initialize your object using __init__,
class SavingsAccount:
def __init__(self, name, initial_deposit):
self.accountNumber = 12345
self.savingsAccounts = {self.accountNumber : [name, initial_deposit] }
print("Account creation has been successful. Your account number is ", self.accountNumber)
saving_account = SavingsAccount(name='a', initial_deposit=4)
print(saving_account.savingsAccounts)
Also, most of the Pythonistas prefer snake_casing while naming variables.
You are creating a new instance of SavingsAccount with every call. After you call to createAccount completes, that instance is garbage-collected, as there are no references to it stored anywhere.
s = SavingsAccount()
s.createAccount(name='a', initialDeposit=4)
print(s.savingsAccounts[12345])
(See Taohidul Islam's answer for how you should be defining the class, though.)
The line that gives the error does this actions:
Calls SavingsAccount.init() to create the object
Asks for the item 12345 in the dictionary (that whas just created so it's empty)
You should structure your code in a different way. You should have a list of accounts or similar that is unique, and then insert in it the accounts you create.
Must first initialize an instance of your SavingsAccount class
#initialize savings account object
s = SavingsAccount()
#call created account method
s.createAccount(name="a", initialDeposit=4)
#print the account
print(s.savingsAccounts[12345])
Although your datastructure is confusing, why not have one instance of a savings account object represent an individuals account? Then you could just assign member variables for values you want to track.
class SavingsAccount:
def __init__(self, name, initial_deposit):
self.account_name = name
self.bal = initial_deposit
def deposit(self, val):
self.bal += val
def return_account(self):
return self.__dict__
Now you can use it more simplistically
s = SavingsAccount("name", 500)
s.deposit(500)
acc = s.return_account()
print(acc)
>> {"account_name": "name", "bal": 1000}

How to print actual name of variable class type in function?

I'm trying to return variable name, but i keep getting this:
<classes.man.man object at (some numbers (as example:0x03BDCA50))>
Below is my code:
from classes.man import man
def competition(guy1, guy2, counter1=0, counter2=0):
.......................
some *ok* manipulations
.......................
if counter1>counter2:
return guy1
bob = man(172, 'green')
bib = man(190, 'brown')
print(competition(bob , bib ))
Epilogue
If anyone want to, explain please what I can write instead of __class__ in example below to get variable name.
def __repr__(self):
return self.__class__.__name__
Anyway, thank you for all of your support
There are different ways to approach your problem.
The simplest I can fathom is if you can change the class man, make it accept an optional name in its __init__ and store it in the instance. This should look like this:
class man:
def __init__(number, color, name="John Doe"):
self.name = name
# rest of your code here
That way in your function you could just do with:
return guy1.name
Additionnally, if you want to go an extra step, you could define a __str__ method in your class man so that when you pass it to str() or print(), it shows the name instead:
# Inside class man
def __str__(self):
return self.name
That way your function could just do:
return guy1
And when you print the return value of your function it actually prints the name.
If you cannot alter class man, here is an extremely convoluted and costly suggestion, that could probably break depending on context:
import inspect
def competition(guy1, guy2, counter1=0, counter2=0):
guy1_name = ""
guy2_name = ""
for name, value in inspect.stack()[-1].frame.f_locals.items():
if value is guy1:
guy1_name = name
elif value is guy2:
guy2_name = name
if counter1 > counter2:
return guy1_name
elif counter2 > counter2:
return guy1_name
else:
return "Noone"
Valentin's answer - the first part of it at least (adding a name attribute to man) - is of course the proper, obvious solution.
Now wrt/ the second part (the inspect.stack hack), it's brittle at best - the "variables names" we're interested in might not necessarily be defined in the first parent frame, and FWIW they could as well just come from a dict etc...
Also, it's definitly not the competition() function's responsability to care about this (don't mix domain layer with presentation layer, thanks), and it's totally useless since the caller code can easily solve this part by itself:
def competition(guy1, guy2, counter1=0, counter2=0):
.......................
some *ok* manipulations
.......................
if counter1>counter2:
return guy1
def main():
bob = man(172, 'green')
bib = man(190, 'brown')
winner = competition(bob, bib)
if winner is bob:
print("bob wins")
elif winner is bib:
print("bib wins")
else:
print("tie!")
Python prints the location of class objects in memory if they are passed to the print() function as default. If you want a prettier output for a class you need to define the __repr__(self) function for that class which should return a string that is printed if an object is passed to print(). Then you can just return guy1
__repr__ is the method that defines the name in your case.
By default it gives you the object type information. If you want to print more apt name then you should override the __repr__ method
Check below code for instance
class class_with_overrided_repr:
def __repr__(self):
return "class_with_overrided_repr"
class class_without_overrided_repr:
pass
x = class_with_overrided_repr()
print x # class_with_overrided_repr
x = class_without_overrided_repr()
print x # <__main__.class_without_overrided_repr instance at 0x7f06002aa368>
Let me know if this what you want?

Tuple error with class and object

I'm currently working on this,
Design a class Car.
This class has the following attributes: maker, model, and price
The Car class should have methods to return the current values of these attributes. In addition, this class has a method update_price to change the price attribute value.
Design a class called Dealer.
This class has the following attributes: address and inventory. The inventory attribute is a list of Car objects.
The Dealer class should have methods to add a car object to the inventory, remove a car object from the inventory (car removal should be done based on car model), and update the price of a certain car model. This class also has a method that shows the entire inventory and a method that calculates the total value of all cars in the inventory.
In the main function, the program creates a Dealer object, adds multiple cars to the inventory, and shows the current inventory. Then the program removes a car model from the inventory and updates the price for a given car model. At the end, the program displays the entire inventory and the total value of all cars currently in the inventory.
But when I run my code I get
TypeError: descriptor 'append' requires a 'list' object but received a 'tuple'
And I have no idea how to fix this
class Car:
def __init__(self, maker, model, price):
self.__maker=maker
self.__model=model
self.__price=price
def get_model(self):
return self.__model
def get_maker(self):
return self.__maker
def get_price(self):
return self.__price
def create_list(list):
maker=input('Enter Car maker: ')
model=input('Enter Car model: ')
price=float(input('Enter Car price: $'))
account=(maker, model, price)
list.append(account)
def update_price(self):
self.__price=price
return self.__update_price
class Dealer():
def __init__(address, inventory):
self.__inventory=inventory
self.__address=address
def update_price(list):
model=input('Enter car model: ')
account=Car(maker, model, price)
found='false'
for model in list:
if account.get_model() == model:
account.append(price)
found=='true'
if found == 'false':
print('Model Not Found')
return account
def show_inventory(list):
show_inventory=print(account)
def calculate_total_value(list):
print(sum(self.__account.price))
def remove_car(list):
model=input('Model for removal')
found = 'false'
for model in list:
if account.get_model() ==model:
account.remove(model)
found =='true'
if found =='false':
print('Model Not Found')
def get_address(address):
self.__address=address
return self.__address
def main():
address=input('Enter address: ')
account_list=[]
counter=1
manytimes=float(input('How many cars to add? '))
while counter<=manytimes:
counter=counter+1
create_list(list)
show_inventory(account_list)
remove_car(account_list)
show_inventory(list)
update_price(account_list)
show_inventory(account_list)
calculate_total_value(account_list)
main()
Looks like wherever that error is occuring, you're trying to append something to a tuple which is immutable, meaning it can't be changed (in this case, you can't append something to it). A tuple is very similar to a list in that in can store values and be accessed through an index (starting at 0). Wherever you're trying to append something, make sure you're using a list and not a tuple. If it is a tuple, you can convert it to a list using list(tuple_name) and then you should be able to append to it.

Missing 1 required positional argument (object created and dictionary initialized)

I see this question has been asked before in different variations, but I feel as though I have implemented the feedback I have seen in those threads (mainly making sure the object is created as I have done in the second last night, and making sure the dictionary is initiated, which I feel I have done in the third line) and I am still receiving the error. Any advice would be very much appreciated. Thank you!
class Groceries:
def __init__(self, grocery_list):
self.grocery_list = {}
def add_item(self, item):
item = input("name: ")
purchased = input(False)
self.grocery_list[item] = purchased
something = Groceries()
something.add_item()
The error I get is:
Traceback (most recent call last):
File "intermediate_python.py", line 14, in <module>
something = Groceries()
TypeError: __init__() missing 1 required positional argument: 'grocery_list'
I tried to resolve this error by removing grocery_list from the def init statement (not sure why this would work, I was just playing around) and the error was moved to the next line with:
Traceback (most recent call last):
File "intermediate_python.py", line 15, in <module>
something.add_item()
TypeError: add_item() missing 1 required positional argument: 'item'
To initialise an instance of your Groceries class you need to provide the parameter "grocery_list" which is defined in your init method
class Groceries:
def __init__(self, grocery_list):
self.grocery_list = {}
Try create your instance using the following:
something = Groceries({"milk": 1, "butter": 1})
Alternatively, to allow for empty Groceries objects, you can add a default parameter to the init method, you should also be assigning the value of the parameter to the variable self.grocery_list instead of assigning it an empty dictionary:
class Groceries:
def __init__(self, grocery_list={}):
self.grocery_list = grocery_list
and also with your method add_item, again you need to pass a parameter or else removed the parameter (since you're asking for it as input)
def add_item(self):
item = input("name: ")
purchased = input("amount: ")
self.grocery_list[item] = purchased
something = Groceries({"milk": 0})
something.add_item()
>> name:
>>> milk
>> amount:
>>> 1
def __init__(self, grocery_list):
self.grocery_list = {}
Although you are passing a grocery_list to your constructor, you are not actually using it to set the value. You merely need to set self.grocery_list equal to the passed argument grocery_list to fix this as I do below.
def add_item(self, item):
item = input("name: ")
You are also trying to both pass item as an argument and input it manually in the function. This means that regardless of what input you send to add_item, you will always override that value with user input. You can fix this by making the item argument optional and checking if it was passed before prompting the user for input as I do below.
purchased = input(False)
self.grocery_list[item] = purchased
Also, it is unclear here why you are fetching input with an implicit conversion of the boolean value False to string as a second prompt after asking for the item name. It seems that you want to set some purchased value to True or False, but it is also unclear then why you are using a dictionary for making a list of groceries in the first place unless you intend to include the quantity as the dictionary value. If you just wanted to see if an item was on the grocery list you could use the set() type instead of dict(). This might serve as an example to help you move forward if you want to maintain quantities.
class Groceries:
def __init__(self, grocery_list={}):
self.grocery_list = grocery_list
def add_item(self, item='', qty=1):
if not item: item = input('name: ')
if item in self.grocery_list:
self.grocery_list[item] += qty
else:
self.grocery_list[item] = qty
def item_qty(self, item=''):
if not item: item = input('name: ')
return(self.grocery_list[item])
def print_all(self):
for key, val in self.grocery_list.items():
print(val, key)
something = Groceries()
something.add_item('apple') # add one apple to groceries
print(something.item_qty('apple'))
something.add_item('apple', 3) # add three apples to groceries
print(something.item_qty('apple'))
something.add_item() # add arbitrary item to groceries
something.print_all() # print all groceries
If you are creating an instance of the Groceries class (something = Groceries(*args)), at the end of the initialization, the Groceries.__init__() function gets called with the same argument list *args.
That means, when you write something = Groceries(), you are trying to call the __init__ function without arguments. On the other hand, the function has a required positional argument grocery_list (any positional argument is required if you didn't provide a default for it).
Since your code defines the grocery_list as an empty dictionary anyway, you don't need to have this an argument to your __init__ function, you can simply use:
def __init__(self):
self.grocery_list = {}
Which will fix the error.
Or you can have a default value and use:
def __init__(self, grocery_list={}):
self.grocery_list = grocery_list
Which would let you use __init__ without providing the grocery_list argument, and using {} in that case.
The exact same case holds for the add_item function: you are defining it with a required positional argument, and then you are trying to call it without any argument. Since you aren't passing any value to the function on the function call that you would later use, you can again remove the argument and simply use:
def add_item(self):

Accessing elements of lists, and calling their fuctions

Here is Customer class:
class Customer:
def __init__(self, timestamp, cid, item_count):
self.time_stamp = timestamp
self.customer_name = cid
self.item_count = item_count
def checkout(self, new_timestamp):
self.time_stamp = new_timestamp
def get_cus_name(self):
return self.customer_name
If I create an empty list of Customer objects like:
customers = [Customer]
And then somewhere else I try to call Customer methods in a loop like:
def checkout_customer(self, cid):
for cus in self.customers:
if cus.get_cus_name == cid:
cus.checkout(self.cur_num_customers + 7)
why do I get an error when I try to call cus.checkout? My ide tells me that it expects a Customer but got an int. Why doesn't it pass itself into the 'self' arg here?
However if I just create a Customer object and directly call its methods, it works fine:
def foo(self):
cus = Customer(1,'pop',2)
cus.checkout(23)
This is my first time learning python, and ive been stuck trying to figure out lists, and accessing its members. Perhaps my initialization of self.custormers = [Customer] is incorrect?
EDIT:
In my constructor of tester class I create an empty list like this:
self.customer = [Customer]
I am able to add customers no problem:
def add_custormer(self, customer):
self.customers.append(customer)
My problem is not adding customers, but accessing their methods once they are in a list. Doing something like this self.customers[0].checkout(1,'pop',2) gives me an error "Expected type 'Customer' got int".
I am not sure of the class where checkout_customer lives but I am assuming you declare the list self.customers somewhere in it.
self.costumers = []
If you intend to add an element Customer to the list you should use something like: self.customers.append(Customer(x,y,z)) since you want to add a new customer to the list and when doing so you are required to initialize the Customer class.
I didn't try the code but I believe something like this should work:
def foo(self):
self.customers.append(Customer(1,'pop',2))
self.checkout_customers(23)

Categories