NameError: global name 'collection' is not defined - python

As an assignment, I'm trying to create two classes: One class, Book, looks to see if a book is checked out and returns the title, author and page numbers in a book (these are input variables) and the other class, called Library, adds title - author pairs to a dictionary and sees if some particular book is checked out or not. I keep getting an error message every time I try to run it. How do I fix this strange error?
Here is my code:
class Book:
def __init__(self, title, author, pages):
self.title = title
self.author = author
self.pages = pages
self.checkedOut = False
def checked_Out(self):
print(self.checkedOut)
return self.checkedOut
def change_value_of_checkedOut(self):
if self.checkedOut == False:
self.checkedOut = True
print("Switched from False to True.")
elif self.checkedOut == True:
self.checkedOut = False
print("Switched from True to False.")
def return_pages(self):
print(self.pages)
return self.pages
def return_title(self):
print(self.title)
return self.title
class Library:
def __init__(self):
collection = {}
def addExistingBook(self, book):
collection[book.title] = book.author
def addNewBook(self, title, author, pages):
new_book = Book(title, author, pages)
collection[title] = new_book.author
def change_checked_out_status(self, title):
if title in collection.keys():
title.change_value_of_checkedOut()
else:
print("This book is not in the collection.")
def main():
title = str(input("Enter the title of the book. "))
author = str(input("Enter the author of the book. "))
pages = int(input("Enter the number of pages in the book. "))
myBook = Book(title, author, pages)
myLib = Library()
myLib.addExistingBook(myBook)
main()
Here is what happens when I try to run it:
Enter the title of the book. The Count of Monte Cristo
Enter the author of the book. Alexandre Dumas
Enter the number of pages in the book. 1250
Traceback (most recent call last):
File "C:/Python33/Class Programs/book_library_classes.py", line 56, in <module>
main()
File "C:/Python33/Class Programs/book_library_classes.py", line 54, in main
myLib.addExistingBook(myBook)
File "C:/Python33/Class Programs/book_library_classes.py", line 36, in addExistingBook
collection[book.title] = book.author
NameError: global name 'collection' is not defined

You defined collection as a local variable in __init__:
def __init__(self):
collection = {}
But that doesn't magically make it an instance variable. You have to do that explicitly:
class Library:
def __init__(self):
self.collection = {}
def addExistingBook(self, book):
self.collection[book.title] = book.author
Also, I wouldn't make methods like this:
def return_title(self):
print(self.title)
return self.title
They're just another layer of obfuscation over the straightforward book.title attribute.
Also, you don't need to write .keys(). if key in dictionary is the preferred syntax:
if title in self.collection:

Add self.collection where ever you are referencing collection.

Class members must be accessed using self.propertyName. Your code should look like this:
class Library:
def __init__(self):
self.collection = {}
def addExistingBook(self, book):
self.collection[book.title] = book.author
....

In your Collection class you have to use internal collection:
class Library:
def __init__(self):
self.collection = {}
def addExistingBook(self, book):
self.collection[book.title] = book.author
Also the last line looks suspicious. Did you mean this:
self.collection[book.title] = book

collection is a local variable
Try referencing it in your functions with self.collection, which should fix your problem.

you should tell python that collection belongs to the Library instance, myLib:
modify the Library Class to say: self.collection everywhere you currently have collection
i.e.
class Library:
def __init__(self):
self.collection = {}
def addExistingBook(self, book):
self.collection[book.title] = book.author
def addNewBook(self, title, author, pages):
new_book = Book(title, author, pages)
self.collection[title] = new_book.author
def change_checked_out_status(self, title):
if title in self.collection.keys():
title.change_value_of_checkedOut()
else:
print("This book is not in the collection.")
Hope this helps!

Related

Python. How to define an Items object within my inventoryRead method and then Call the inventoryRead method

In my inventoryRead how can I properly define an Items object. Also, how do I run this method
Traceback (most recent call last):
File "C:/Users/jburk/OneDrive/Desktop/Deft Project/quickMart.py", line 7, in <module>
class Items:
File "C:/Users/jburk/OneDrive/Desktop/Deft Project/quickMart.py", line 34, in Items
inventoryRead('self')
File "C:/Users/jburk/OneDrive/Desktop/Deft Project/quickMart.py", line 24, in inventoryRead
item1 = Items()
NameError: name 'Items' is not defined
code
class Items:
# Constructor to initilize an item object
# An item has name, quantity, price, member price, and taxing status
def __init__(self, name, quantity, price, memPrice, taxStatus):
self.name = name
self.quantity = quantity
self.price = price
self.memPrice = memPrice
self.taxStatus = taxStatus
def inventoryRead(self):
txt = ""
count = 0
f = open("inventory.txt", "r")
inList = []
item1 = Items()
print(item1)
for line in f.readlines():
item1.name = line[0:line.find(":")]
print(item1)
print(item1.name)
inList[count] = Items()
txt = line.next()
print(txt)
inventoryRead('self')
#arr = f.readlines()
#print(arr[0])
I think it would be smarter if you had an Item and a separate ItemMangager or Items class. I'm going to call it Items from now on.
Items would contain some store every Item in (for example) a list and Items loads them from the file, but also saves them to same.
You try to create an instance of the class you are currently in, the main way you would edit this file is through the self prefix you used before to modify this instances attributes.
The example I provide is done using a .txt file for storage to keep to your way of doing it, although it would probably be smarter to use an actual database module like sqlite, you should have a look into that.
#!/usr/bin/env python3
#Item holds one record of sth
class Item():
def __init__(self,name, quantity, price, memPrice, taxStatus):
self.name = name
self.quantity = quantity
self.price = price
self.memPrice = memPrice
self.taxStatus = taxStatus
class Item_Manager:
def __init__(self):
self.items=[]
self.data_file_path="records.txt"
def load_data(self):
with open(self.data_file_path,"r") as f:
contents= f.read()
for i in contents.split("\n"): #splits every line into a new string
data=i.split(" ") #asuming every line has the attributes space-separated
item=Item(data[0],data[1],data[2],data[3],data[4])
self.items.append(item) #saving the new created Item instance in the mangagers items list
def save_data(self): #overwriting
with open(self.data_file_path,"w") as f:
for i in self.items: #i = item instance
f.write("{} {} {} {} {}".format(i.name,i.quantity,i.price,i.memPrice,i.taxStatus))
def create_item(self):
name=input("Name: ")
quantity= input("Quantity: ")
price=input("Price: ")
memPrice= input("MemPrice: ")
taxStat = input("Tax Status: ")
item=Item(name, quantity, price, memPrice, taxStat)
self.items.append(item)
def display_data(self):
for i in self.items:
print(i.name,i.quantity,i.price,i.memPrice,i.taxStatus)
Hope this helps, if you have any further questions, just comment under this.
There are two main issues here:
Your code is obviously faulty, see below, and
You have made some strange design choices.
Code Error:
Your error comes from line: inventoryRead('self')
This is wrong because:
you do not write self into a class method call. It is passed by itself.
Your method call is outside of context. It is not part of another method in the class, while it obiously isn't an attribute.
Sort off "working" version of your code would be:
class Items:
''' Constructor to initilize an item object
An item has name, quantity, price, member price, and taxing status'''
def __init__(self, name=None, quantity=0, price=0, memPrice=0, taxStatus=None): # fill the default values or you will have an error in inventory function
self.name = name
self.quantity = quantity
self.price = price
self.memPrice = memPrice
self.taxStatus = taxStatus
def inventoryRead(self):
txt = ""
count = 0
f = open("inventory.txt", "r")
inList = []
item1 = Items() # <-- works like this only if you have defaults in your __init__ definition
print(item1)
for line in f.readlines():
item1.name = line[0:line.find(":")]
print(item1)
print(item1.name)
inList[count] = Items()
txt = line.next()
print(txt)
items = Items()
items.inventoryRead()
This would work now but it surelly isn't what you want...
Design choice
You have an Item --> make it a class
You have an inventory? --> make it a class!
Manipulate your code from within Inventory
You should really read some examples and tutorials on python OOP, since it would seem you don't really feel comfortable with how it works.
Now for a quick working example:
#!/usr/bin/env python3
class Item:
def __init__(self,name, quantity=0, price=0, memPrice=0, taxStatus=0):
self.name = name
self.quantity = quantity
self.price = price
self.memPrice = memPrice
self.taxStatus = taxStatus
class Inventory:
def __init__(self):
self.items = []
def inventoryRead(self):
# your function to collect items from file
if __name__ == '__main__':
inventory = Inventory()
inventory.inventoryRead()

Searching for an ID in a list of books

I've made a simple library system which stores books with an ID, name and cost. My question is rather simple but my limited knowledge of python has let me down.
I've created a class that stores books in the library, they are created like this;
if __name__ == '__main__':
lib = Library()
book1 = Book(1, 'Bookname1', "$30")
book2 = Book(2, 'Bookname2', "$10")
book3 = Book(3, 'Bookname3', "$40")
I have to make a function that searches for a book by its ID, by making a function in my library class. I tried to make it like in the code below, but it didn't work. basically, I want to give my function an ID, and it should return the name and cost of that particular book, but only if the ID is present in the list.
class Book:
def __init__(self, ID, name, price):
self.ID = ID
self.name = name
self.price = price
def show(self):
print(self.ID, self.name, self.price)
def get_attribute_string(self):
print(str(self.ID) + '_' + str(self.name) + '_' + str(self.price))
def get_id(self):
print(self.ID)
def get_name(self):
print(self.name)
def get_price(self):
print(self.price)
class Library:
def __init__(self):
self.books = []
def add_book(self, Book):
self.books.append(Book)
def remove_book(self, Book):
self.books.remove(Book)
#def show_id(self, ID):
# if ID in lib:
# return self.books
def show_all(self):
for Book in self.books:
Book.show()
if __name__ == '__main__':
lib = Library()
book1 = Book(1, 'Bookname1', "$30")
book2 = Book(2, 'Bookname2', "$10")
book3 = Book(3, 'Bookname3', "$40")
#1.show_id
lib.add_book(book1)
lib.add_book(book2)
lib.add_book(book3)
lib.remove_book(book2)
lib.show_all()
I think the simplest idea if you need ID indexing is to use a dictionary:
class Library:
def __init__(self):
self.books = dict()
def add_book(self, Book):
self.books[Book.ID] = Book
def remove_book(self, Book):
del self.books[Book.ID]
def get_book(self, ID):
return self.books[ID]
def show_id(self, ID):
self.get_book(ID).show()
def show_all(self):
for Book in self.books.values():
Book.show()
You could even rename get_book to __getitem__, this second name is special in python, it's called a dunder method (or magic method). Implementing it will allow you to write lib[id] instead of lib.show_id(id) (I'm not saying that you should, but that's an option). There are many other dunder methods that you can try using for fun, you can find some of them in the python data model.
I think that you should post your code on codereview as you may use broader advices on your code.
As noted above, there might be better implementations, but using your current code, you can do the below where I have adapted your show_id function according to requirements. It relies on a list comprehension to identify the correct IDs.
Hope this helps!
class Book:
def __init__(self, ID, name, price):
self.ID = ID
self.name = name
self.price = price
def show(self):
print(self.ID, self.name, self.price)
def get_attribute_string(self):
print(str(self.ID) + '_' + str(self.name) + '_' + str(self.price))
def get_id(self):
print(self.ID)
def get_name(self):
print(self.name)
def get_price(self):
print(self.price)
class Library:
def __init__(self):
self.books = []
def add_book(self, Book):
self.books.append(Book)
def remove_book(self, Book):
self.books.remove(Book)
def show_id(self, ID):
# simple list comprehension to filter; note that you might need to make sure that e.g. all IDs are int, or str
matching_ids = [book for book in self.books if book.ID == ID]
# return name, price tuple for all matching books as requested -- note that this will return a list of all matches
# if the IDs are truly unique, you can return e.g. a single tuple or Book object here
# if nothing found, will return empty list
return [(book.name, book.price) for book in matching_ids]
def show_all(self):
for Book in self.books:
Book.show()
if __name__ == '__main__':
lib = Library()
book1 = Book(1, 'Bookname1', "$30")
book2 = Book(2, 'Bookname2', "$10")
book3 = Book(3, 'Bookname3', "$40")
#1.show_id
lib.add_book(book1)
lib.add_book(book2)
lib.add_book(book3)
lib.remove_book(book2)
lib.show_all()
print(lib.show_id(1))
Even if this method is not really optimized, you can go through them with a for loop.
for book in lib.books:
if book.id == searched_id:
searched_book = book
break

After adding to a list, can only print most recent object

I want to add an object to a list, the object contains both a title and an artist.
The object adds fine, but when I want to print all the objects from my list I get an error. When doing my method as shown below, it prints only the most recent object added twice.
listOfBooks = []
class Book:
title = "No Title"
author = "No Author"
def myBook(self, title, author):
self.title = title
self.author = author
def get(self):
return self.title + self.author
book = Book()
for _ in range(0,2):
titleInput = input("Enter a title: ")
authorInput = input("Enter an author: ")
book.myBook(titleInput, authorInput)
listOfBooks.append(book)
for i in range(0,len(listOfBooks)):
print(listOfBooks[i].get())
You need to create a new Book instance in every loop iteration. Move book = Book() inside the loop:
for _ in range(0,2):
book = Book() # Here
titleInput = input("Enter a title: ")
authorInput = input("Enter an author: ")
book.myBook(titleInput, authorInput)
listOfBooks.append(book)
OK, so you have some instance variables (title and author) that are specific to each individual Book object. The typical way to set these while creating your instances is to ask for them in the object's __init__() method, so your code would look like this:
listOfBooks = []
class Book:
title = "No Title"
author = "No Author"
def __init__(self, title, author):
self.title = title
self.author = author
def get(self):
return self.title + self.author
for _ in range(0,2):
titleInput = input("Enter a title: ")
authorInput = input("Enter an author: ")
# Calling the class calls the __init__ method and creates the
# instance
book = Book(titleInput, authorInput)
listOfBooks.append(book)
for i in range(0,len(listOfBooks)):
print(listOfBooks[i].get())
This gives the same results as Yu Hao's answer:
Enter a title: Green Eggs
Enter an author: Seuss
Enter a title: Moby Dick
Enter an author: Melville
Green EggsSeuss
Moby DickMelville
boot initial(book = Book()) is out of the for loop, so, actually, you edit the same Book instance every loop, you can use id() to have a look.
for list, elements can be same, so regarding your for loop, the same object book is appended into listOfBooks for twice, that's why print the same output for twice.

How do I get the return value of my __str__() function to print in my main?

I need to create two classes for a program that takes in input variable for a book, title, author, number of pages and looks to see if it's checked out and then adds books to a dictionary in the following format dictionary[title] = author. Then I need to print al of my information in a main function in this format: ""title author pages checkedOut." I basically need to print out the return value of my __str__() function in my classes. Here is my code:
class Book:
def __init__(self, title, author, pages):
self.title = title
self.author = author
self.pages = pages
self.checkedOut = False
def checked_Out(self):
print(self.checkedOut)
return self.checkedOut
def change_value_of_checkedOut(self):
if self.checkedOut == False:
self.checkedOut = True
print("Switched from False to True.")
elif self.checkedOut == True:
self.checkedOut = False
print("Switched from True to False.")
def return_pages(self):
print(self.pages)
return self.pages
def return_title(self):
print(self.title)
return self.title
def __str__(self):
return ("Title: " + self.title + "Author: " + self.author +
"Pages: " + self.pages + "Checked Out Status: " +
self.checkedOut)
class Library:
def __init__(self):
self.collection = {}
def addExistingBook(self, book):
self.collection[book.title] = book.author
def addNewBook(self, title, author, pages):
new_book = Book(title, author, pages)
self.collection[title] = new_book.author
def change_checked_out_status(self, title):
if title in self.collection:
title.change_value_of_checkedOut()
else:
print("This book is not in the collection.")
def __str__(self):
for myBook in self.collection[myBook]:
self.collection[myBook]
self.collection[myBook] = self.author
# I want to print this return value in my main
return ("Title: " + self.title + "Author: " + self.author
+ "Pages: " + self.pages + "Checked Out Status: " + self.checkedOut)
def main():
title = str(input("Enter the title of the book. "))
author = str(input("Enter the author of the book. "))
pages = int(input("Enter the number of pages in the book. "))
myBook = Book(title, author, pages)
myLib = Library()
myLib.addExistingBook(myBook)
myLib2 = Library()
myLib3 = myLib2.__str__()
print(myLib3)
main()
It seems like something is wrong with the for loop in my __str__(self) function in the class Library (the idea is that I want the loop to iterate over ever book in the collection) but I'm not sure what the problem is. Here is the error message that I get:
Enter the title of the book. A Tale of Two Cities
Enter the author of the book. Charles Dickens
Enter the number of pages in the book. 434
Traceback (most recent call last):
File "C:\Python33\Class Programs\lab8.py", line 71, in <module>
main()
File "C:\Python33\Class Programs\lab8.py", line 68, in main
myLib3 = myLib2.__str__()
File "C:\Python33\Class Programs\lab8.py", line 54, in __str__
for myBook in self.collection[myBook]:
UnboundLocalError: local variable 'myBook' referenced before assignment
You are looping over the keys of a dictionary, you should not try to pass in a key in the loop statement:
def __str__(self):
for myBook in self.collection:
It is unclear what you are trying to do with that loop. You are then accessing self.collection[myBook] without doing anything with the return value, then you are replacing the value with self.author (which doesn't exist on self, that is still the library), then you return something completely different as the __str__() result.
I think you want to create a list of your library books here:
def __str__(self):
return ', '.join('{} - {}'.format(title, author) for title, author in self.collection.items())
Now you return a comma-separated list of all titles and authors in your collection.
There are 3 things going on there:
Using a generator expression, we loop over the dictionary .items() method; this generates (key, value) pairs for everything in the dictionary.
Each key and value is passed to the str.format() method, using the string as a template to put a - dash between the title and author pairs stored in your self.collection dictionary.
The whole loop is passed to the str.join() method; this method takes all the strings from the sequence given to it and concatenates them together with ', ' commas between them.
Next, you are creating an empty library, then print the output of the __str__() method; there is no need to call that explicitly, print() does that for you. Do not create a new library, just print the one you already have:
myLib = Library()
myLib.addExistingBook(myBook)
print(myLib)
I think you want to change
def __str__(self):
for myBook in self.collection[myBook]:
self.collection[myBook]
self.collection[myBook] = self.author
to
def __str__(self):
for myBook in self.collection.keys():
self.collection[myBook]
self.collection[myBook] = self.author

How do I fix these errors for a short assignment in creating classes in Python?

I have a short assignment I need to complete that has to do with creating classes, and I'm not sure what the problem with my code is. Here are the instructions below and underneath that is my code. Please explain what my error(s) is/are and how to fix it/them:
Build 2 classes. The first class will be a "Book" class. The book class has 4 variables that are all private. The first is checkedOut which is a Boolean value initialized to false, title which is a string that is initalized by a input variable, author which is a string and is initialized by an input variable, pages that is an integer and is also initialized by an input variable. This class will also have 4 functions associated with is. The first will return the variable checkedOut. The second will change the value of checkedOut. If the value is set to true, then it will be changed to false and vice versa. The third function will return the number of pages and the final function will return the title. When a book object is printed it be in the format of "title author pages checkedOut".
The second class will be called a library. When the library is initialized it will create a empty dictionary called collection. The library class will have 2 functions. The first function will be called addBook and it will take in 3 input variables, title, author, and pages. In this function you will create a book object, then add it to the dictionary with the title as the key. The second function will take in the title of a book, find the book in the dictionary and call the books function that changes the checkedOut status. The finally, when a library object is printed out, it will print out each book in the library on a separate line.
Finally the library class will be implemented in a python program called main.py.
Here is my code:
class Book:
title = str(input("Enter the title of the book. "))
author = str(input("Enter the author of the book. "))
pages = int(input("Enter the number of pages in the book. "))
checkedOut = False
def checked_Out(self):
print(checkedOut)
return checkedOut
def change_value_of_checkedOut(self):
if checkedOut == False:
checkedOut = True
print("Switched from False to True.")
elif checkedOut == True:
checkedOut = False
print("Switched from True to False.")
def return_pages(self):
print(pages)
return pages
def return_title(self):
print(title)
return title
class Library(Book):
def __init__(self):
collection = {}
def addBook(self, title, author, pages):
new_book = Book()
collection[title] = author
def change_checked_out_status(self, title):
if title in collection:
new_book.change_value_of_checkedOut(self)
else:
print("This book is not in the collection.")
What is wrong with what I did here? I keep getting errors to the effect that a certain variable name is not defined when I try to create objects and run the code in IDLE.
(0) Next time, paste the errors.
class Book:
title = str(input("Enter the title of the book. "))
author = str(input("Enter the author of the book. "))
pages = int(input("Enter the number of pages in the book. "))
checkedOut = False
def checked_Out(self):
print(checkedOut)
return checkedOut
(1) Just this part.
checkedOut is not defined. Where do you see checkedOut? right above the function checked_Out. Okay. Short answer, add self..
Example:
def checked_Out(self):
print(self.checkedOut)
return self.checkedOut
And you really should not be doing the title, author stuff there. They become class variables, not instance variables. There's a difference.
(2) Avoid input if you are still using 2.x Python.
Use raw_input and get rid of str. That's safer. In the 3.x you can use input and it will always be string (raw_input will always return string). That's also causing problem.
(3) You have the tenadacy of captializing everything. I am usually pretty chill but it kinds of bad. Don't call it checked_Out that's really inconsistent plus Python programmers prefer checked_out. checkedOut can be named to checked_out and hey, you can conflict. Don't name your function and variables so similar.
Create the class separately, and get the input separately. Also, you want to mention that each variable is an instance variable, not in the local scope:
class Book:
def __init__(self, title, author, pages):
self.title = title
self.author = author
self.pages = pages
self.checkedOut = False
def checked_Out(self):
print(self.checkedOut) # it has to be the instance variable
return self.checkedOut
def change_value_of_checkedOut(self):
if self.checkedOut == False:
self.checkedOut = True
print("Switched from False to True.")
elif self.checkedOut == True:
self.checkedOut = False
print("Switched from True to False.")
def return_pages(self):
print(self.pages)
return self.pages
def return_title(self):
print(self.title)
return self.title
class Library:
def __init__(self):
collection = {}
def addExistingBook(self, book):
collection[book.title] = book.author
def addNewBook(self, title, author, pages): # create a book
new_book = Book(title, author, pages)
collection[title] = new_book.author # access the author
def change_checked_out_status(self, title):
if title in collection.keys():
title.change_value_of_checkedOut()
else:
print("This book is not in the collection.")
Then, add the rest in a main function:
def main():
# if you are using Python 2.x, change input() to raw_input()
title = str(input("Enter the title of the book. "))
author = str(input("Enter the author of the book. "))
pages = int(input("Enter the number of pages in the book. "))
myBook = Book(title, author, pages)
myLib = Library()
myLib.addExistingBook(myBook)

Categories

Resources