Can't change instance attribute of my class? - python-3.x

I'm starting out in python and I can't quite figure out why I'm unable to change the data stored in one of my instance attributes. I have the following code:
class Bank:
def __init__(self, bank = 'Unnamed'):
self.bank = bank
self.clients = []
self.status = {'bank': self.bank, 'clients': self.clients}
self.c_counter=0
def deposit(self, name = None, amount = 200):
self.name = name
self.amount = amount
self.c_counter += 1
if self.name is None:
self.name = print("client" + str(self.c_counter));
self.clients.append((self.name, self.amount))
else:
self.clients.append((self.name, self.amount))
This produces the following output:
bb = Bank("bb")
bb.deposit(amount = 2000)
bb.status
out: {'bank': bb, 'clients': [(None, 2000)]}
While the desired output is:
out: {'bank': bb, 'clients': [('client1', 2000)]}
As you can see, what I'm trying to do is to set the client name to "clientx" if client name is not specified during a deposit; the x is just a number to distinguish each generic client from other generic clients without a specific name.
If a specific name is not provided when calling the deposit attribute of the bank the client name is set to None by default. I check this with the if condition and change the client name accordingly, but for some reason the client name literally gets added as "None" to the list of clients in the dictionary (list of tuples). What is wrong here?

print prints a string to sys.stdout and returns None. you should change the line
self.name = print("client" + str(self.c_counter));
to
self.name = "client" + str(self.c_counter)
or maybe self.name = f"client{self.c_counter}" for python >= 3.6.

print is a NoneType, it's None and you can prove it:
>>> type(print())
<class 'NoneType'>
>>>
And:
>>> print(print())
None
>>>
So print isn't be used for assigning, print is used for outputting stuff, whereas in this case you're assigning stuff, that which isn't meant for print to handle, so change:
self.name = print("client" + str(self.c_counter));
To:
self.name = "client" + str(self.c_counter)
Or:
self.name = "client%s" % self.c_counter
Or:
self.name = "client{}".format(self.c_counter)
Or if your python version is bigger or equal to 3.6, you can use:
self.name = f"client{self.c_counter}"

Related

AttributeError Problem with Multiple inheritance in python

I wanted to calculate the Total and Average Marks of a student with multiple inheritances in python. But whenever I create an object for my child class it gives me access to all the methods of parent classes but shows an AttributeError when I call the method of the child class. I tried to use the super() function but the result is the same.
I just paste my code below. Can you suggest to me a solution to that?
class Student_Marks:
def __init__(self):
# initializing variables
self.__roll: int
self.__marks1: int
self.__marks2: int
def getMarks(self):
self.__roll = int(input("Enter Roll No: "))
self.__marks1, self.__marks2 = map(int, input("Enter Marks: ").split())
return self.__roll, self.__marks1, self.__marks2
class Cocurricular_Marks:
def __init__(self):
self.__activemarks: int
def getActiveMarks(self):
self.__activemarks = int(input("Enter Co Curricular Activities Marks: "))
return self.__activemarks
class Result(Student_Marks, Cocurricular_Marks):
def __init__(self):
super().getMarks()
super().getActiveMarks()
def display(self):
total = self.__marks1 + self.__marks2 + self.__activemarks
avg = total / 3
print("Roll No: ", self.__roll)
print("Total Marks: ", total)
print("Average Marks: ", avg )
# creating Objects
res = Result()
res.getMarks()
res.getActiveMarks()
res.display() # I got problem here
You're prepending the attributes with two underscores in the classes, this mangles the name of the attribute (see the documentation on Private Variables).
For instance, Student_Marks's __roll will be mangled to _Student_Marks__roll as you exit getMarks.
Hence Result.display() not being able to access self.__marks1, but it can access it as self._Student_Marks__marks1.
See the following minimal example.
class K:
__a = 1
class KK(K):
pass
k = K()
k.__a # AttributeError: 'K' object has no attribute '__a'
k._K__a # 1
kk = KK()
kk._K__a # 1

Why class atribute doesn't change on update

I am wondering why the class attribute doesn't change in the code below. As you can see the value remains the same, despite being changed in class A?
class A:
valueA = 1.05
class User:
def __init__(self,name):
self.name = name
self.value = A.valueA
user = User('Alice')
print(user.value)
A.valueA = 1.1
print(A.valueA)
print(user.value)
output:
1.05
1.1
1.05
I don't understand why it should? ValueA is an number which is an immutable object(everything is a object), so valueA is just some sort of referencethat points to value 1.05.
To make more clear, here is an example of how does it behave:
class A(object):
val = [1,2,3]
class B(object):
def __init__(self):
self.myval = A.val
print(A.val)
# prints [1,2,3]
obj = B()
print(obj.myval)
# prints [1,2,3] because its starts to point to the same list
A.val[0] = 5
print(obj.myval)
# prints [5,2,3] because both still points to the same list,
# and you just changed it fist value
A.val = [4,5,6,7]
print(A.val)
# prints new list [4,5,6,7]
print(obj.myval)
# prints [5,2,3] because it still points to old list.
obj2 = B()
print(obj2.myval)
# prints new list [4,5,6,7] because assignment was done after A.val changed
also here is a good article about variables in python https://realpython.com/python-variables/#object-references

Problem retrieving individual objects in pickled dictionary (Python 3)

My program stores "food" objects that are pickled into a dictionary and stored in a csv file, which acts as a database. I want to retrieve individual food objects on command from the dictionary, but when I attempt to I seem to only retrieve the last object in the dictionary.
import pickle
class Food(object):
fooddict = dict({})
def __init__(self, name, weight, calories, time):
self.name = name
self.weight = weight
self.calories = calories
self.time = time
def __str__(self):
return '{self.name}s'.format(self=self) + \
' weigh {self.weight}'.format(self=self) + \
' ounces, contain {self.calories}'.format(self=self) + \
' calories, and stay fresh for {self.time}'.format(self=self) + \
' days.'
#classmethod
def createFoodInput(cls):
name = str(input("Enter the name: "))
weight = float(input("Enter the weight: "))
calories = float(input("Enter the calories: "))
time = float(input("Enter how many days it can store for: "))
return cls(name, weight, calories, time)
def storeFoodDict(f):
fooddict = Food.retreiveFoodDict()
if fooddict == "Empty File":
fooddict = dict({f.name: f})
with open("food.csv", 'wb') as filewriter:
try:
pickle.dump(fooddict, filewriter)
except:
print("Error storing pickled dictionary")
else:
food_found = False
for key in list(fooddict):
if key.__eq__(f.name):
print("Food already stored!")
food_found = True
if not food_found:
fooddict.update({f.name: f})
with open("food.csv", 'wb') as filewriter:
try:
pickle.dump(fooddict, filewriter)
except:
print("Error storing pickled dictionary")
#classmethod
def retreiveFoodDict(cls):
with open("food.csv", 'rb') as filereader:
try:
fooddict = pickle.load(filereader)
return fooddict
except EOFError:
return("Empty File")
def findFood(title):
fooddict = Food.retreiveFoodDict()
for key in list(fooddict):
if key.__eq__(title):
continue
return fooddict[key]
s = "apple"
n = findFood(s) #does not work, it returns banana instead of apple
#which is really just grabbing whatever is the
#last object in the dictionary
m = findFood("banana") #seems to work, but only because banana is the
#last object in the dictionary
print(n) #should print an apple "food object" but instead prints a banana
print(str(m.calories)) #works, but if I said n.calories it would still print
#m.calories instead
p = Food.retreiveFoodDict() #seems to work and retrieve the dictionary
print(str(p)) #also seems to work of course
Console Output:
bananas weigh 5.0 ounces, contain 120.0 calories, and stay fresh for 3.0 days.
120.0
{'apple': <main.Food object at 0x00D2C2E0>, 'banana': <main.Food object at 0x00D36D00>}
The dictionary contains 2 food objects (apple and banana), but the print(n) statement shows a banana, not an apple. Can anyone point out why this is or what I am misunderstanding? Thank you so much!
I found the answer to my own problem. I was misusing the continue in my findFood function.
This code solved my issues.
def getFood(food_name):
fooddict = Food.retreiveFoodDict()
for key in list(fooddict):
if key.__eq__(food_name):
return fooddict[key]
What this function does is simply retrieve a dictionary of objects in a csv file and iterates through the keys until the passed key name is located. If found, the proper key name will be returned as a food object. My original mistake was using the "continue" keyword to stop the for-loop, which was returning the object directly after the one we wanted.

How to print a dictionary made up of lines from a file in python3?

Any help is much appreciated! Thanks
I have a dictionary made up of lines extracted from a file like this:
Danny Shalev, 050-1111111, aaa#aaa.com
Gil Rom, 050-2222222, bbb#bbb.com
Tal Yakir, 050-3333333, ccc#ccc.com
Edit: my goal is for the dict to be printed out like this:
Danny Shalev - 050-1111111 - aaa#aaa.com
Gil Rom - 050-2222222 - bbb#bbb.com
Tal Yakir - 050-3333333 - ccc#ccc.com
The first name is the key, and the rest are the values.
I have written the code for converting the file lines into a dict, and I want to print out all values from my dictionary in a specific format, which would be line by line, separated by "-". I have already written the function print_person, to print it out in this format, I just want to apply this function (from the previous class) into my dict.
Here's the code:
class Person:
def __init__(self, name, phone,email):
self.name = name
self.phone = phone
self.email = email
def print_person(self):
return (str(self.name)+" - "+str(self.phone)+" - "+str(self.email))
class AddressBook:
def __init__ (self):
self.contactsdict = {}
def add(self, newContact):
self.contactsdict[newContact.name] = newContact.phone + " - " + newContact.email
def search(self, name):
return (self.contactsdict.get(name))
def addFromFile(self, fileName):
f = open("contacts.txt")
for line in f:
(key, val, val2) = line.split(",")
self.contactsdict[key] = val + " - " + val2
f.close
def printAddressBook(self):
for key, val in self.contactsdict.items():
Person.print_person
address = AddressBook() # make an instance
p1=Person("Danny Shalev","050-1111111","aaa#aaa.com")
print (p1.print_person())
address.add(p1)
address.addFromFile("contacts.txt")
address.printAddressBook()
I believe the problem is in this section, since I don't know how to use the method:
def printAddressBook(self):
for key, val in self.contactsdict.items():
Person.print_person
This
for key, val in self.contactsdict.items():
Person.print_person
deconstructs all your dictionary entries into 2 variables, one the key, the other the value. The second line is incorrrect - Person is your class, you need an instance of the class to use the defined print method on it.
You can call val.print_person() on each instance of the class Person to print each IF you store Persons in your inner dictionary. Classes are "templates" how a class is constructed - the instance must be used to call its functions. Currently your code only stores string in the internal dictionary.
To add persons to your internal Dict replace
for line in f:
(key, val, val2) = line.split(",")
self.contactsdict[key] = val + " - " + val2
with
for line in f:
(key, val, val2) = line.split(",")
self.contactsdict[key] = Person(key,val,val2) # create instances of Persons
# and store them in the dictionary by there name
# you get collisions if your file contains persons with identical names
Fixed code (this and some other errors marked with comments):
class Person:
def __init__(self, name, phone,email):
self.name = name
self.phone = phone
self.email = email
def print_person(self):
return (str(self.name) + " - " + str(self.phone) + " - " + str(self.email))
class AddressBook:
def __init__(self):
self.contactsdict = {}
def add(self, newContact):
self.contactsdict[newContact.name] = newContact # store the Person instance
# under its name as key
def search(self, name):
return (self.contactsdict.get(name))
def addFromFile(self, fileName):
f = open("contacts.txt")
for line in f:
(key, val, val2) = line.split(",")
self.add(Person(key,val,val2)) # create Person and use own add-Function
# to add it to internal dictionary
f.close
def printAddressBook(self):
for key, val in self.contactsdict.items():
print( val.print_person() ) # you need to print the output
# of print_person() - it currently only
# returns the string and does not print it
address = AddressBook() # make an instance
p1 = Person("Danny Shalev","050-1111111","aaa#aaa.com")
print(p1.print_person())
address.add(p1)
address.addFromFile("contacts.txt")
address.printAddressBook()
Search returns a person, so you can use it to change persons inside your dict:
print("")
p = address.search("Danny Shalev")
p.name = "Hallo" # change the name of the found person (the key will still be "Danny Shalev")
address.printAddressBook()
Output:
Danny Shalev - 050-1111111 - aaa#aaa.com
Gil Rom - 050-2222222 - bbb#bbb.com
Tal Yakir - 050-3333333 - ccc#ccc.com
Hallo - 050-1111111 - aaa#aaa.com # after change of searched person
Gil Rom - 050-2222222 - bbb#bbb.com
Tal Yakir - 050-3333333 - ccc#ccc.com

Global Name "msg" not defined

I'm currently writing a class called SMS_store(). In it, I have a method called delete.
Delete is simply supposed to make sure the user has given me a valid integer. If so, it's supposed to pop an item from the list.
class SMS_store():
def __init__(self):
self.__inbox = []
def delete(self, i):
if i >= len(self.__inbox):
return None
else:
self.__inbox.pop[i]
Whenever I run the code in my test program, I run into two errors at my delete stage:
1) if I type myInbox.delete(2) when there's only 2 items in the list, I get "list index out of range" and I though I was protected from that error. myInbox.delete(3) gives me None.
2) If I type myInbox.delete(1) when there's a valid index 1 in my list, it says global name 'msg' not defined. I don't get why I'm seeing that error.
Here's my full class code.
#SMS_store class
"""
Pre-condition: SMS_store class is instantiated in client code.
Post-condition: SMS_store class is instantiated.
"""
class SMS_store():
#Object instantiation
"""
Pre-conditon: SMS_store class is instantiated in client code.
Post-condition: Object creates an empty list.
"""
def __init__(self):
self.__inbox = []
#add_new_arrival method
"""
Pre-condition: Class method is handed a valid phone number of 11, 10, or 7
digits as a string with no hyphens or letters, a string containing a time,
and a string containing the text of a message.
Post-condition: Method will append a tuple containing False for an
undread message, the phone number, the time arrived and the text of the
message to the class created list.
"""
def add_new_arrival(self, from_number, time_arrived, text_of_SMS):
number = from_number
#Check for valid phone number and add hyphens based on number length
if len(number) == 11:
number = number[0] + "-" + number[1:4] + "-" + number[4:7] + "-"\
+ number[7:]
elif len(number) == 7:
number = number[:3] + "-" + number[3:]
elif len(number) == 10:
number = "1-" + number[:3] + "-" + number[3:6] + "-" + number[6:]
elif number.isalpha():
number = "Invalid number"
else:
number = "Invalid number"
time = time_arrived
text = text_of_SMS
message = (False, number, time, text)
self.__inbox.append(message)
#message_count method
"""
Post-condition: method returns the number of tuples in class created list.
Returns None if list is empty.
"""
def message_count(self):
count = len(self.__inbox)
if count == 0:
return None
else:
return count
#get_unread_indexes method
"""
Post-condition: method creates an empty list,checks for any tuples with
"False" at index 0. If "False" is found, it appends the index for the
tuple in the list. Method returns list of indexes.
"""
def get_unread_indexes(self):
unread = []
for message in self.__inbox:
if message[0] == False:
unread.append(self.__inbox.index(message))
return unread
#get_message method
"""
Pre-condition: Method is passed an integer.
Post-condition: Method checks for a valid index number. If valid, the
method will then check if indexed tuple contains "True" or "False" at index
0. If True, message is returned in new tuple containing items from indexes
1, 2, and 3. If False, a new tuple is created containing "True"
indicating the message is now read, plus indexes 1, 2, and 3 from the
original called tuple.
"""
def get_message(self, i):
#check for valid index number
if i >= len(self.__inbox):
return None
else:
msg = self.__inbox[i]
if msg[0] == True:
return (msg[1], msg[2], msg[3])
#create new tuple with True, and index 1-3 from original tuple
else:
self.__inbox.pop(i)
newMsg = (True, msg[1], msg[2], msg[3])
self.__inbox.insert(i, newMsg)
return newMsg[1:]
#delete method
"""
Pre-condition: Method is passed an integer.
Post-condition: Method checks that the integer is a valid index number. If
valid, method pops index from class created list.
"""
def delete(self, i):
if i >= len(self.__inbox):
return None
else:
self.__inbox.pop(i)
#Clear method
"""
Post-condition: method resets the inbox to an empty list.
"""
def clear(self):
self.__inbox = []
Here's how I am using the code in my test program:
#Test instantiation
naomisInbox = SMS_store()
martisInbox = SMS_store()
#Test add_new_arrival
naomisInbox.add_new_arrival("12345678912", "10:38PM", "Yay! Sorry, been")
martisInbox.add_new_arrival("23456789123", "10:37PM", "Hey I finally hit 90")
martisInbox.add_new_arrival("12345678912", "10:40PM", "Now I sleep :)")
naomisInbox.add_new_arrival("23456789123", "10:40PM", "Night")
#Test message_count
count = naomisInbox.message_count()
print("Naomi has", count, "messages in her inbox.")
count = martisInbox.message_count()
print("Marti has", count, "messages in his inbox.\n")
#Test get_unread_indexes
numUnread = naomisInbox.get_unread_indexes()
print("Naomi has unread messages at indexes: ", numUnread)
numUnread = martisInbox.get_unread_indexes()
print("Marti has unread messages at indexes: ", numUnread,"\n")
#Test get_message
msg = naomisInbox.get_message(9)
print("Getting message from Naomi's inbox at index [9]: ")
if msg == None:
print("No message at that index.")
else:
for item in msg:
print(item)
print("\n")
numUnread = naomisInbox.get_unread_indexes()
print("Naomi now has unread messages at indexes: ", numUnread, "\n")
msg = martisInbox.get_message(1)
print("Getting message from Marti's inbox at index [1]:")
for item in msg:
print(item)
print("\n")
numUnread = martisInbox.get_unread_indexes()
print("Marti now has unread messages at indexes: ", numUnread, "\n")
#Test delete
remove = naomisInbox.delete(0)
if remove == None:
print("Invalid index.")
count = naomisInbox.message_count()
numUnread = naomisInbox.get_unread_indexes()
print("Naomi now has", count, "messages with unread messages at index: ",\
numUnread)
#Test clear
print("\nAfter clearing: ")
naomisInbox.clear()
count = naomisInbox.message_count()
print("Naomi now has", count, "messages in her inbox.")
martisInbox.clear()
count = martisInbox.message_count()
print("Marti now has", count, "messages in his inbox.")
Error
Error:
Traceback (most recent call last):
File "/home/theriddler/Documents/CSIS153/Assignments/Nansen3/Nansen3.py", line 56, in <module>
remove = naomisInbox.delete(0)
File "/home/theriddler/Documents/CSIS153/Assignments/Nansen3/modSMS.py", line 125, in delete
NameError: global name 'msg' is not defined
Any help is appreciated. Sorry if it's a repeated question. Thanks, Blackwell.
for your first problem.
1)if there are only two items in the list then you cannot delete the 2nd item by passing 2 as index it should be 1.
2)your second problem tells that you are using same msg variable in SMS_store class within different functions without defining it as self variable for the class. However cant find any thing for now. You should probably check it again as it works well on my machine.
Now a little more light on your delete method:
def delete(self, i):
if i >= len(self.__inbox):
return None
else:
self.__inbox.pop(i)
Here if you want to delete the last message always then just use self.__ibox.pop() without passing any index but in case you want to delete an indexed message then u should do self.__ibox.pop(i-1)
because in case i is last element of the list then it will always be equal to length of the list and else will never be executed.
Also your delete method returns None only in if condition but if else runs then again None is returned by default so
remove = naomisInbox.delete(0)
if remove == None:
print("Invalid index.")
This will always print 'invalid index' as message even if the message gets deleted.

Resources