Python class doesn't see second object in list(attribute) - python

I want to code movie classification class, which can find (based on some criterias), add and print movies.
Here is my code:
class Movie:
def __init__(self,name,director,year,location):
self.name = name
self.director = director
self.year = year
self.location = location
self.information = {'name':self.name,'director':self.director,'year':self.year,'location':self.location}
def get_name(self):
return self.name
def get_director(self):
return self.director
def get_year(self):
return self.year
def get_location(self):
return self.location
def get_information(self):
return self.information
def __str__(self):
return f"Name = {self.name},director = {self.director},year = {self.year},location = {self.location}"
class Classification:
def __init__(self):
self.movie_list = []
def length(self):
return len(self.movie_list)
def __getitem__(self,key):
if isinstance(self.movie_list,slice):
return self.movie_list[key]
def add_movie(self,movie):
self.movie_list.append(movie)
def print_movie(self):
for movie in self.movie_list:
print(movie)
def find_movie(self,**kwargs):
check_list = []
for movie in self.movie_list:
for name,value in kwargs.items():
if movie.get_information()[name] == value:
check_list.append(True)
else:
check_list.append(False)
if all(item == True for item in check_list):
print(movie)
check_list.clear()
Here i have a class Movie and Classification; Classification has only 1 attribute, which is a list of movies. But i have two problems:
a = Movie('Matrix','Dan Yefimov','1999','New York')
b = Movie('Legend','Mak Markus','2005','Kiev')
clasif = Classification()
clasif.add_movie(a)
clasif.add_movie(b)
clasif.find_movie(location = 'New York')
find_movie() works for the first movie (a in our case) in the list. But for the second one it prints nothing, even when I enter correct parameters for a search.
Slicing doesn't work. There is no error message, it just prints nothing.
Can you help me with my problems?
P.S I would like also to hear some general advices about improving of my code.

By removing the unnecessary methods, we can shorten the code considerably. We can also just pass __getitem__ through to the list. I would also use __len__ over defining a length method.
class Movie:
def __init__(self,name,director,year,location):
self.name = name
self.director = director
self.year = year
self.location = location
def __str__(self):
return f"Name = {self.name},director = {self.director},year = {self.year},location = {self.location}"
class Classification:
def __init__(self):
self.movie_list = []
def __len__(self): # __len__ lets you do len(classif)
return len(self.movie_list)
def __getitem__(self,key):
return self.movie_list[key]
def add_movie(self,movie):
self.movie_list.append(movie)
def print_movie(self):
for movie in self.movie_list:
print(movie)
def find_movie(self,**kwargs):
for movie in self.movie_list:
if all(hasattr(movie, k) and getattr(movie, k) == v for k, v in kwargs.items()):
print(movie)

Here is a version of your code will a little less code:
class Movie:
ATTRIBUTES = ('name', 'director', 'year', 'location')
def __init__(self, name, director, year, location):
self.name = name
self.director = director
self.year = year
self.location = location
def __str__(self):
return ', '.join(
'{} = {}'.format(attr_name, getattr(self, attr_name))
for attr_name in self.ATTRIBUTES)
class Classification:
def __init__(self):
self.movie_list = []
def __len__(self):
return len(self.movie_list)
def __getitem__(self, key):
return self.movie_list[key]
def add_movie(self, movie):
self.movie_list.append(movie)
def print_movies(self):
for movie in self.movie_list:
print(movie)
def find_movies(self, **kwargs):
for movie in self.movie_list:
do_print = True
for attr_name, attr_value in kwargs.items():
if attr_name in Movie.ATTRIBUTES:
if getattr(movie, attr_name) != attr_value:
do_print = False
if do_print:
print(movie)
I added a class attribute called ATTRIBUTES to Movie; this is used in Movie.__str__() and also in Classification.find_movies(). It is a suggestion to avoid repeating the attributes a lot in the code.
In the method Classification.find_movies() I check that it is a valid attribute before I compare it to the movie instance. Invalid parameters are ignored, but you could change the code so that invalid parameters automatically cause the movies to not be printed (all will be excluded).

Related

Keep getting the ID from items in a list which is in a class

class EditorState:
def __init__(self, content):
self.content = content
class Editor:
def __init__(self):
self.content = ""
def __str__(self):
return f'{self.content}'
def setContent(self, value):
self.content = value
def createContent(self):
return EditorState(self.content)
def restore(self, new_value):
self.content = new_value
def getcontent(self):
return self.content
class History:
def __init__(self):
self.history = []
def __repr__(self):
return self.history
def push(self, value):
self.history.append(value)
def remove(self):
my_list = self.history
my_list.pop()
last_index = my_list[-1]
return last_index
def getvalue(self):
my_list = self.history
return self.history
editor = Editor()
history = History()
editor.setContent("a")
history.push(editor.createContent())
editor.setContent("b")
history.push(editor.createContent())
editor.setContent("c")
history.push(editor.createContent())
editor.setContent("D")
history.push(editor.createContent())
editor.restore(history.remove())
print(history.getvalue())
print(editor.getcontent())
OUTPUT that I get when I check the Items in the list: [<main.EditorState object at 0x0000017B77360040>, <main.EditorState object at 0x0000017B773600D0>, <main.EditorState object at 0x0000017B77360130>]
The output I want: [a,b,c]
I've learned how to use the Memento pattern in java, and I wanted to try the pattern with python. I does work but the problem is that when I'm returning the last item from my list in the history class, so it keeps showing me its id not the value. It's the same when I print the list using the getvalue() method.
I've tried to use the magic methods sush as str or repr but it did'nt work, also I've tried to set the attribut to a variable but no results.
Fixed it :
class EditorState:
#change here
def returnContent(self,content):
return content
class Editor():
content = '' #change here
def __init__(self):
self.content = ""
def __str__(self):
return f'{self.content}'
def setContent(self, value):
self.content = value
def createContent(self):
return EditorState.returnContent(self,self.content) #Change here
def restore(self, new_value):
self.content = new_value
def getcontent(self):
return self.content
class History:
history = [] #change here
def __init__(self):
self.history = []
def __repr__(self):
return self.history
def push(self, value):
self.history.append(value)
def remove(self):
my_list = self.history
my_list.pop()
last_index = my_list[-1]
return last_index
def getvalue(self):
my_list = self.history
return my_list
editor = Editor()
history = History()
editor.setContent("a")
history.push(editor.createContent())
editor.setContent("b")
history.push(editor.createContent())
editor.setContent("c")
history.push(editor.createContent())
editor.setContent("D")
history.push(editor.createContent())
editor.restore(history.remove())
print(history.history) #change here
print(editor.getcontent())
Output:
This function (in 2nd pic below) returned an object of the class instead on the variable because init() functions return only empty/None datatype(This is a Rule and the arrow mark show what datatype is being returned), so it basically returned an object which was pushed into your list
Here you can see init() returns nothing.
Here you can see what datatype is being pushed into the list
Also try creating variables globally in a class to access them easily when needed anywhere.

Remove/add object into list

Can my coding work? The program should search for the hotel in the list, and if it returns None, I can add a new Hotel into the list.
I need help on the str part especially.
class Hotel:
def __init__(self, name, address):
self._name = name
self._address = address
#property
def name(self):
return self._name
#property
def address(self):
return self._address
def __str__(self):
return "Hotel Name: {} Address: {}".format(self._name, self._address)
class TransportServices:
def __init__(self):
self._hotels = []
#self._bookings = [] # as you can see i have two other list in this class
#self._drivers = []
def searchHotel(self, name):
for h in self._hotels:
if h.name == name:
return h
return None
def addHotel(self, hotel):
h = self.searchHotel(hotel)
if h is None:
self._hotels.append(hotel)
return True
else:
return False
def __str__(self):
hotels = [str(h) for h in self._hotels]
return "Hotels\n{} ".format(self._hotels) # need help here
def main():
hotel = TransportServices()
hotel.addHotel(Hotel("123", "ABC"))
hotel.addHotel(Hotel("234", "QWE"))
print(hotel)
main()
The problem is in your
def searchHotel(self, name):
for h in self._hotels:
if h.name == name:
return h
return None
method. You're comparing h.name to name, but name is not a string (like h.name), it's a Hotel object.
Try instead
def searchHotel(self, hotel):
for h in self._hotels:
if h.name == hotel.name:
return h
# return None is not necessary here, it's the default.
or else expect a string to be passed here and then in addHotel do:
h = self.searchHotel(hotel.name)
Note also that defining __eq__ can be useful if two things should be logically the same. If any two Hotels share a name and an address, they should be the same hotel, so you can do
class Hotel:
def __eq__(self, other: 'Hotel') -> bool:
if not isinstance(other, self.__class__):
return False
return (self.name, self.address) == (other.name, other.address)
This has the benefit of making searchHotel much easier.
def searchHotel(self, hotel):
if hotel in self._hotels:
return hotel
it think because you are just printing the TransportServices object list, try to use the searchHotel function.
print(hotel.searchHotel("234"))
The problem is in your return statement.
Change
def __str__(self):
hotels = [str(h) for h in self._hotels]
return "Hotels\n{} ".format(self._hotels) # need help here
to
def __str__(self):
hotels = [str(h) for h in self._hotels]
return "Hotels\n{} ".format(hotels)

Functions in Class Methods: Where to Raise Errors?

Writing a function to reshelve books where the Library attempts to reshelve a book currently held by the indicated Patron. The book is moved from patron's borroweds to the Library's available books list. I coded most of it, but I'm not sure if my loop is correct and I have no idea where to insert my missingIdError, any suggestions? Note: I also have a Patron Class and defined them already along with the ID so no need to worry about the missing class.
class DuplicateIdError (Exception):
def __init__(self, id, category = "Book" or "Patron"):
self.id = int(id)
self.category = str(category)
def __str__(self):
return 'duplicate {} ID: #{}'.format(self.category,self.id)
def __repr__(self):
return 'duplicate {} ID: #{}'.format(self.category,self.id)
class MissingIdError (LookupError):
def __init__(self, id, category = "Book" or "Patron"):
self.id = int(id)
self.category = str(category)
def __str__(self):
return 'duplicate {}: {}'.format(self.id, self.category)
def __repr__(self):
return 'duplicate {}: {}'.format(self.id, self.category)
class Library:
def __init__(self, books=None, patrons=None):
self.books = []
self.patrons = []
def __str__(self):
return "Library(<{}>, <{}>)".format(self.self.books, self.patrons)
def __repr__(self):
return "Library(<{}>, <{}>)".format(self.self.books, self.patrons)
#Returns None.
#Raises a DuplicateIdError if there's already a book in the library with that id#.
#Raises a MissingIdError if the patron or book wasn't found
def reshelve_book(self, patron_id, book_id):
for patron in self.patrons:
for book in self.books:
try:
if book.book_id == book_id:
raise DuplicateIdError(book_id,"Book")
elif book.book_id != book_id:
self.books.append(book)
book.borroweds.remove(book)
Just a couple of simple guards would give you the other exception:
def reshelve_book(self, patron_id, book_id):
if patron_id not in self.patrons:
raise MissingIdError(patrod_id, 'Patron')
I don't understand book.borroweds.remove(book) but presumable you need to test book_id has been borrowed.
Do you really need a loop?
def reshelve_book(self, patron_id, book_id):
if patron_id not in self.patrons:
raise MissingIdError(patrod_id, 'Patron')
if book_id in self.books:
raise DuplicateIdError(book_id,"Book")
try:
book = self.patrons[patron_id].borrowed_books[book_id]
except KeyError:
raise MissingIdError(book_id, 'Book')
self.books[book_id] = book
del self.patrons[patron_id].borrowed_books[book_id]
BTW: your constructors for the exceptions don't do what I think you think it does:
def __init__(self, id, category = "Book" or "Patron"):
Is equivalent to do:
def __init__(self, id, category = True):
I would just avoid putting a default arg for category:
def __init__(self, id, category):

Printing a list from a class in Python

My simplified code is below: it creates an animal, and places it inside a zoo. I want to print the list of animals within the zoo. Going round in circles with this!
class Animal(object):
def __init__(self, name):
self.name = name
class Zoo(object):
def __init__(self):
self.animals = []
def __str__(self):
rep = ", ".join(self.animals)
return rep
def add(self, name):
self.animals.append(Animal(name))
def main():
while True:
zoo = Zoo()
animal = input("add an animal: ")
zoo.add(animal)
print(zoo)
main()
The added __repr__ Method to the Animal returns us the name.
The zoo = Zoo() has to be outside of the loop, this makes sure that we do not create a new zoo with every iteration.
Then we print the list (zoo.animals).
class Animal(object):
def __init__(self, name):
self.name = name
def __repr__(self):
return self.name
class Zoo(object):
def __init__(self):
self.animals = []
def __str__(self):
rep = ", ".join(self.animals)
return rep
def add(self, name):
self.animals.append(Animal(name))
def main():
zoo = Zoo()
while True:
animal = input("add an animal: ")
zoo.add(animal)
print(zoo.animals)
main()
You can simply refer to the name property of Animal in your Zoo.__str__(), e.g.:
def __str__(self):
return ', '.join(animal.name for animal in self.animals)
Now print(zoo) should work correctly.
However this doesn't provide a lot of encapsulation if say you wanted to change what it means to print an animal, e.g. height, size, etc. So perhaps a more encapsulated form would be:
class Animal(object):
...
def __str__(self):
return self.name
class Zoo(object):
...
def __str__(self):
return ", ".join(str(animal) for animal in self.animals)
Now when you print(zoo) the Animal class is responsible for its own string presentation.
Just as a note: you probably should create the Animal instance outside of Zoo, what happens if you decide to create a class hierarchy of Animals (e.g. Mammal) that has different behaviours, your Zoo class would only know about Animals.
class Animal(object):
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
class Zoo(object):
def __init__(self):
self.animals = []
def __str__(self):
return ", ".join(str(animal) for animal in self.animals)
def add(self, animal):
self.animals.append(animal)
def main():
zoo = Zoo()
while True:
animal = Animal(input("add an animal: "))
zoo.add(animal)
print(zoo)
main()
This would still behave properly if you create a Mammal class:
class Mammal(Animal):
...
zoo.add(Mammal(...))
print(zoo)

Python - How to sort a list, by an attribute from another class

I have some elements in a list in one class. I want them sorted in a new list, and they have to be sorted by an attribute from another class.
Can anyone give an example?
My code so far looks like this:
class Carcompany:
def __init__(self, model, production_number):
self.model = model
self.production_number = production_number
self.car_list = []
def add_car_to_car_list(self, car):
self.car_list.append(car)
class Info:
def __init__(self):
self.license_plate_number = []
def add_license_plate_to_list(self, license_plate):
self.license_plate_number.append(license_plate)
I need self.car_list to be sorted by self.license_plate_number - highest number first. I don't know how much I'm missing to get there. I appreciate any help I can get :)
To sort a list of objects that have the attribute bar:
anewlist = sorted(list, key=lambda x: x.bar)
You say that you have classes already (show them!) so you can make them sortable by defining __lt__:
class Car(object):
def __init__(self, year, plate):
self.year = year
self.plate = plate
# natural sort for cars by year:
def __lt__(self, other):
return self.year < other.year
def __repr__(self):
return "Car (%d) %r" % (self.year, self.plate)
class Plate(object):
def __init__(self, val):
self.val = val
def __repr__(self):
return repr(self.val)
# natural sort by val:
def __lt__(self, other):
return self.val < other.val
cars = [ Car(2009, Plate('A')),
Car(2007, Plate('B')),
Car(2006, Plate('C'))
]
print cars
print sorted(cars) # sort cars by their year
print sorted(cars, key=lambda car: car.plate) # sort by plate

Categories