Python Object Test - python

I have the following unittest questions I am trying to pass.
def test_map2(self):
self.home = []
self.home.append(Bed('Bedroom'))
self.home.append(Sofa('Living Room'))
self.home.append(Table('Bedroom'))
mapping = map_the_home(self.home)
self.assertTrue(isinstance(mapping['Bedroom'][0], Bed))
self.assertTrue(isinstance(mapping['Living Room'][0], Sofa))
self.assertTrue(isinstance(mapping['Bedroom'][1], Table))
Every value should be a list, with one or more Furnishing subclass instances inside.
This is my current try.
class Furnishing(object):
def __init__(self, room):
self.room = room
class Sofa(Furnishing):
name = 'Sofa'
class Bed(Furnishing):
name = 'Bed'
class Table(Furnishing):
name = 'Table'
def map_the_home(home):
results = {}
for furnitiure in home:
if furnitiure.room in results:
results[furnitiure.room] = (results[furnitiure.room],furnitiure)
else:
results[furnitiure.room] = furnitiure
return results
def counter(home):
counter_list = {}
for line in home:
if line.name in counter_list:
print(line.room,line.name)
counter_list[line.name] = counter_list[line.name] + 1
else:
counter_list[line.name] = 1
for furniture in counter_list:
print('{0} = {1}'.format(furniture,counter_list[furniture]))
if __name__ == "__main__":
home = []
home.append(Bed('Bedroom'))
home.append(Sofa('Living Room'))
home.append(Table('Bedroom'))
map_the_home(home)
counter(home)
The counter is just another part but wanted to give full code. I thought I had this using dict, but as the test says I need to have every value in a list with Furnishing subclass instances inside. Any insight would be great

The test is expecting that the result will be like this:
mapping == {'Bedroom': [bed, table], 'Living Room': [sofa]}
Whereas you create:
{'Bedroom': (bed, table), 'Living Room': sofa}
Note that the "Living Room" value isn't a container, but a single Sofa instance.
Indeed, if you had three items of furniture in a room, e.g. adding a Sofa in the "Bedroom":
{'Bedroom': ((bed, table), sofa), 'Living Room': sofa}
you would keep on nesting deeper.
The minimal fix is:
if furniture.room in results:
results[furniture.room].append(furniture)
else:
results[furniture.room] = [furniture]
Note that using a list and tuple will give the same results, as both can be indexed (although you add (not append) to tuples); I think a list is a better fit here, but your use of tuples wasn't the source of the error.

def map_the_home(home):
results = dict()
for furniture in home:
results.setdefault(furniture.room,[]).append(furniture)
return results
Let's walk through it in pseudocode
home = [Bed('Bedroom'), Sofa('Living Room'), Table('Bedroom')]
map_the_home(home)
results = dict()
for each piece of furniture in the home:
append that piece of furniture to results[furniture.room]
if results[furniture.room] doesn't exist, make it an empty list then append
return the results

Related

Python: how to utilize instances of a class

New to OOP and python, I am struggling enormously to grasp what good classes actually are for. I tried to ask help from a lecturer who said "oh, then you should read about general methods to classes". Been putting in a days work but get no where.
I get it that a class allow you to collect an instance structure and methods to it, like this:
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
def show_list(self):
print(self.item_id, self.item_name)
idA = Items("idA", "A")
idA.show_list()
But what is even the point of a class if there were not MANY instances you would classify? If I have a method within the class, I must hard code the actual instance to call the class for. What if you want a user to search and select an instance, to then do operations to (e.g. print, compute or whatever)??
I thought of doing it like this:
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
def show_list(self):
print(self.item_id, self.item_name)
idA = Items("idA", "A")
idB = Items("idB", "B")
select_item = input("enter item id")
select_item.show_list()
Replacing hard coded variable with input variable doesn't work, probably logically. I then played with the idea of doing it like this:
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
iL = [Items('idA', 'A'), Items('idB', 'B')]
selected_item = input("enter item id")
for selected_item in iL:
print(f'{selected_item.item_id} {selected_item.item_name}')
Now all are called thanks to making it a list instead of separate instances, but how do I actually apply code to filter and only use one instance in the list (dynamically, based on input)?
I would love the one who brought me sense to classes. You guys who work interactively with large data sets must do something what I today believe exist in another dimension.
See examples above^^
It seems you want to find all the instances of a certain element within a class.
This is as simple as:
print([x for x in iL if x.item_id == selected_item])
Now, you may ask why you can't just store the elements of iL as tuples instead of classes. The answer is, you can, but
("idA", "A")
is much less descriptive than:
item_id = "idA"
item_name = "A"
Any code you write with classes, you should in theory be able to write without classes. Classes are for the benefit of the coder, not the end-user of the program. They serve to make the program more readable, which I'm sure you'll find is a desirable property.
Your point here is to lookup for Items instances based on their item_id attribute.
That's a thing to create instances of a class.
It's a completely different thing to search for items objects stored in memory - that is not directly linked to the concept of OOP, classes and instances.
You could use dictionary to store references of your objects and then lookup in your dictionary.
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
def show_list(self):
print(self.item_id, self.item_name)
idA = Items("idA", "A")
idB = Items("idB", "B")
lookup_dict = {"idA": idA, "idB": idB}
select_item = input("enter item id")
found_item = lookup_dict.get(select_item)
if found_item:
found_item.show_list()
else:
print(f"item {select_item} not found")

Python Count function not work on instances of a class?

So Ive been trying to make a method which checks if an entered Item is present in the players inventory. For that I used the count to check if the item exists atleast once. However it seems like that it does not instances of the class. (All the items in the inventory of the player are instance of a classs)
inventory = []
def equipItem(self):
equipinput = input("Chosse Item to equip: ")
#Get Input which item to equip
itemcount = self.inventory.count(equipinput)
#Count how many times the item was found
if itemcount >= 1:
itemindex = self.inventory.index(equipinput)
if self.inventory[itemindex].typeitem == "weapontype":
#Check the item type(is an attribute of the item/Class)
self.inventory[0], self.inventory[itemindex] = self.inventory[itemindex], self.inventory[0]
#Switch position of the item to equip and that of the current item (It is always on the first position)
print("Changed Weapon to " + str(equipinput))
game()
#Go back to the game
else:
self.inventory[1], self.inventory[itemindex] = self.inventory[itemindex], self.inventory[1]
print("Changed Armor to " + str(equipinput))
game()
else:
print(f"{Fore.RED}You do not have this item!{Style.RESET_ALL}")
#Output an error Message in red
game()
Example inventory:
self.inventory = [Wooden Dagger, Steel Sword]
Example input would be then: Steel Sword
Expected Output/inventory:
self.inventory = [Steel Sword, Wooden Dagger]
You are comparing apples to oranges. The returned value of input is type string and the inventory array contains instances of some class (which you have omitted but i will call the "equipment" class).
You could create a __str__(self): method for the omitted "equipment" class like so.
class Equipment:
def __init__(self, name: str):
self.name = name
def __str__(self):
return self.name
You could then use list comprehension to create an array of string representations of the inventory and compare the input to that.
inventory = [Equipment('steel sword'), Equipment('basic axe')]
def equip_item(self):
string_inventory = [str(item) for item in self.inventory] # creates array ['steel sword', 'basic axe']
print(string_inventory.count('basic axe')) # now we can compare strings to strings with the count method
I would also recommend looking into the __repr__(self): magic method.
lastly, if comparing string, I also recommend using the .upper() method so capitalization does not cause problems.
You cannot count instances of a class this way. If you look at the representation of it by printing it, you will see something like <__main__.SteelSword object at 0x7f15e6f97430>. They are obviously different, but are instances of the same class. So you can do:
[isinstance(ins, SteelSword) for ins in self.inventory].count(True)
Now, if you use input(), you will always get a string back, which is different from a class reference: 'SteelSword' != <class '__main__.SteelSword'>
So you need a way to address this issue. What you could do, for example, is to create a dict that takes your class names as a string ('SteelSword') key and the class references as the corresponding values.

Why is it so that I keep getting the error that Driver sponsor is not defined, even though I placed it within a class?

I have been trying my hand at a NASCAR project, where I would have to use a class to create 20 unique vehicles and then have them race ( or to see who would reach 500 miles first, through the means of repeatedly choosing a different speed between 1 and 120 and adding it to an increasing odometer). I made what you see below and ran it, and it boots well into the Python IDLE. However, it will always tell me that NameError: name 'Driver_sponsor' is not defined. See, I have been facing this error for a while now, and I have tried placing the Driver_sponsor list into a class, placing it into the Main def and placing the keyword self. before it. No matter what I did, I faced this error. I am going to go back into my class book to see what I can do, but I am hoping that someone here can tell me what I am missing within my code, since, really, I am extremely lost.
from random import randint
import time
class Car:
def __init__(self,Driver_Name,Sponsor):
self.__Total_Odometer_Miles = 0
self.__Speed_Miles_Per_Hour = 0
self.__Driver_Name = Driver_Name
self.__Sponsor = Sponsor
self.__Driver = ('Drivers name Missing')
self.__Sponsor = ('Sponsor Missing')
self.__Driver_sponsor = {'A.J.Allmendinger:3M','Aric Almirola:Allegiant ','Trevpr Bayne:AMR ','Ryan Blaney:Camping World ','Clint Bowyer:Chevrolet ',
'Chris Buesher:Coca-Cola','Kurt Busch:Coca-light ','Kyle Busch:Credit One ','Landon Cassill:Ford','Matt DiBenedetto:FDP',
'Austin Dillon:','Ty Dillon:','Dale Earnhardt:Jacob Companies ','Chase Elliott: M & M ','Denny Hamlin: Microsoft ',
'Kevin Harvick:GoodYear ','Jimmie Johnson:Nationwide','Erik Jones:SUNOCO','Kasey Kahne:Toyota','Matt Kenseth:Visa ' }
def Name(self,Driver_Name):
self.__Driver_Name = Driver_Name
def Retrieve_Name(self):
return self.__Driver_Name
def __mutualize__(self):
self.__Total_Odometer_Miles = 0
self.__Speed_Miles_Per_Hour = 0
def sponsors(self):
self.__Driver_sponsor = {'A.J.Allmendinger:3M','Aric Almirola:Allegiant ','Trevpr Bayne:AMR ','Ryan Blaney:Camping World ','Clint Bowyer:Chevrolet ',
'Chris Buesher:Coca-Cola','Kurt Busch:Coca-light ','Kyle Busch:Credit One ','Landon Cassill:Ford','Matt DiBenedetto:FDP',
'Austin Dillon:','Ty Dillon:','Dale Earnhardt:Jacob Companies ','Chase Elliott: M & M ','Denny Hamlin: Microsoft ',
'Kevin Harvick:GoodYear ','Jimmie Johnson:Nationwide','Erik Jones:SUNOCO','Kasey Kahne:Toyota','Matt Kenseth:Visa ' }
def Retrieve_sponsor(self,Driver_sponsor):
return self.__Driver_sponsor
def main():
for key in Driver_sponsor():
CurrentCar = Car()
CurrentCar.Driver = key
CurrentCar.Sponsor = val
CurrentCar.MPH = randint(1,120)
time.sleep(.05)
time = 5
currentCar.ODT = 5
CurrentCar.ODT = CurrentCar.ODT + CurrentCar.MPH*Time
print(CurrentCar.Driver,CurrentCar.ODT)
if CurrentCar.ODT >= 500:
print('\ the winner is',key,'t\ sponsored by',val)
main()
There are a few issues with your code.
First, you're getting this error because you're calling a variable that isn't set.
But more importantly, you're trying to access the driver-sponsor dict before you've initialized an instance of Car (which currently only happens inside the loop that iterates over Driver_sponsor!).
If you want to loop over driver-sponsor pairs and initialize a new Car for each one, then do you really need the full Driver_sponsor dict initialized for every Car? If so, just pass it as an argument when constructing Car and populate self.__Driver_sponsor.
For example:
driver_sponsor_pairs = {'A.J.Allmendinger:3M',...,'Matt Kenseth:Visa'}
class Car:
def __init__(self, driver_sponsor):
# ...
self.driver_sponsor = driver_sponsor
CurrentCar = Car(driver_sponsor=driver_sponsor_pairs)
# now refer to CurrentCar.driver_sponsor
Second, you are only asking for key when looping over the Driver_sponsor dict, but you call on both key (for Driver) and val (for Sponsor) in each loop . Extract both key and val in your loop creation. You'll need the .items() dict method to get both values:
for key, val in driver_sponsor_pairs.items():
...
Third, your Car __init__ expects Driver and Sponsor arguments, but you try to define CurrentCar = Car() and then populate CurrentCar.Driver and CurrentCar.Sponsor afterwards. Continuing with the above updates, try instead:
CurrentCar = Car(Driver=key, Sponsor=val)
Fourth, you won't need the Retrieve_sponsor() method if you already have the .Sponsor attribute set.
There are a lot of misunderstandings here about Object syntax and design. You may find it frustrating to try and debug at this level of complexity. I would suggest starting very simply, say, with Car() having just one attribute. Test that, make sure it works as you want, and then build more attributes and methods from there.

Adding a Random Item to a List for a Text-Based RPG

This is concatenated to the question I asked earlier today ("List" Object Not Callable, Syntax Error for Text-Based RPG). Now my dilemma resides in adding the herb to the player's herb list.
self.herb = []
is the starting herb list. The function collectPlants:
def collectPlants(self):
if self.state == 'normal':
print"%s spends an hour looking for medicinal plants." % self.name
if random.choice([0,1]):
foundHerb = random.choice(herb_dict)
print "You find some %s." % foundHerb[0]
self.herb.append(foundHerb)
print foundHerb
else: print"%s doesn't find anything useful." % self.name
with foundHerb being the random choice. How do I add this item to the list in a neat way (currently it prints the herb name, then "None") and allow for having several of the same herb?
Here's the herb class:
class herb:
def __init__(self, name, effect):
self.name = name
self.effect = effect
Sample list of herbs (warning: immaturity):
herb_dict = [
("Aloe Vera", Player().health = Player().health + 2),
("Cannabis", Player().state = 'high'),
("Ergot", Player().state = 'tripping')
]
Use a list.
self.herb = []
foundHerb = 'something'
self.herb.append(foundHerb)
self.herb.append('another thing')
self.herb.append('more stuff')
print 'You have: ' + ', '.join(self.herb)
# You have: something, another thing, more stuff
EDIT: I found the code from which you get foundHerb in one of your other questions (please post it in this question too!), which is:
foundHerb = random.choice(herb_dict)
When I look at herb_dict:
herb_dict = [
("Aloe Vera", Player().health == Player().health + 2),
("Cannabis", Player().state == 'high'),
("Ergot", Player().state == 'tripping')
]
This is wrong, use = for assignment. == is for testing equality.
You need to use a function in the second item in these tuples.
Don't add the second item into the list. Like this:
self.herb.append(foundHerb[0])
In your function, think what would happen if random.choice([0,1]) was 0. it would not run the if block, so no herb would ever be chosen. Perhaps in your function, you can return False to say that no herb was found. Then you can do this:
self.herb = []
myherb = collectPlants() # This will either contain a herb or False
if myherb: # If myherb is a plant (and it isn't False)
self.herb.append(myherb)

In python. How do I have a user change a dictionary value, when that dictionary is in a class?

So I had a similar question that was answered in another thread.
How do I update a dictionary value having the user choose the key to update and then the new value, in Python?
Basically, how did one get a nested dictionary value changed via raw_input. I used the solution and it worked well, but I wanted to write the program using classes. So I made a class with a method for editing the dictionary using essentially the same code, however when i try run it in the class method it gives me a "key error" now.
So in the main function this works the solution in the above linked question works great. But in a class method:
class team: # create a class where each team will be an instance
def __init__(self, name):
self.name = name #name of team will be passed from main
self.list_of_players = [] # create a list of the players
self.position1 = {} # create a dictionary for each of the positions on that team
self.position2 = {}
self.roster = [self.position1, self.position2]
def addplayer(self, player_name): # the name of the player is passed to this method from main
print 'add stats' # fill out the appropriate stats through raw_input
stat1 = raw_input('stat1: ')
stat2 = raw_input('stat2: ')
pos = raw_input('POS: ')
vars()[player_name] = {'stat1' : stat1, 'stat2' : stat2, 'POS' : pos} #create a dictionary
# for the player where all his stats are kept
player = {player_name : vars()[player_name]} # create a dictionary that will show the
# player's name as a string and his stats which are held in the dictionary named after him
self.list_of_players.append(player) # append the new player to the list of players
if pos == 'p1': # add the player and his stats to the appropriate position on the team
self.position1[player_name] = player
elif pos == 'p2':
self.position2[player_name] = player
else:
pass
def editplayer(self, player_name): # player's name is passed to the edit function from main
print self.list_of_players # player's name shows up in the list of players for the team
edit_stat = raw_input('which stat? ') # choose which stat(key) to edit via raw input
new_value = raw_input('new value: ') # choose the new value to apply to the chosen key
vars()[player_name][edit_stat] = new_value # here is where it gives a key error! this worked
#in fact even trying to call and print the players name gives the key error.
#player = vars()[player_name]
#print player
def main(): # the main function
loop1 = 0 # creating a loop so one can come back and edit the teams after creating them
list_of_teams = [] # initializing list of teams
while loop1 < 1:
print list_of_teams # show the user what teams are available to choose from
team_option = raw_input('new team or old: ') # create a new team or work with an old one
if team_option == 'new':
team_name = raw_input('team name? ') # get the team name from raw_input
vars()[team_name] = team(team_name) #create an instance of this team name
list_of_teams.append(team_name) # add the team to the list
else:
team_name = raw_input('which team? ') # choose which existing team to work with
player_choice = raw_input('new player or old? ') # choose to create or edit existing player
player_name = raw_input('player_name? ') # choose which player from raw_input
if player_choice == 'new':
vars()[team_name].addplayer(player_name) # give player_name to addplayer method
print vars()[team_name].list_of_players # shows the new player in the appropriate
# instance's roster. This method seems to be working fine
else:
vars()[team_name].editplayer(player_name) # gives the player's name to the editplayer
# method for the appropriate instance. But the player name just raises a key error in
# edit player method. I am baffled.
print vars()[team_name].list_of_players
if __name__ == '__main__':
main()
When it was all one long function this worked but looked like a disaster. Trying to learn better OOP practices but I can't figure out how to call up that dictionary with by the player's name to change the value. I've spent the past few days reviewing tutorials and questions on classes and dictionaries, but clearly I am misunderstanding something about how variables are passed from function to methods.
The fact that it wont even assign the dictionary vars()[player_name] to a var to be printed out means its not recognizing it as the dictionary that was created in the addplayer methond I think. But the fact that it still lists that dictionary in the list of players means it is existing in that instance. So why isn't it recognizing it when i try to address it in the editplayer method? And how do i call up the embeded dictionary created in one method, to change a value in that dictionary in the second method?
Karl pointed out good points that need clarifying: Here's what the attribues I want are.
self.name- i want an instance for each team created
self.list of players - each team should have its own list of players which are dictionaries holding that persons stats. so team1 should have its own list. team2 a different list etc
self.position1/2 - the players on each team would be filed in their various position dictionaries. so Player joe montana's dictionary of statistics would be found in that team's Quarterbacks dictionary
self.roster - should be that team's roster grouped by positions. So a call to print team1.roster should print those players grouped by positions
1) vars() is a dictionary of local variables within a function.
When you are in a method in Python, the contents of the object that you called the method on are not local variables. That's why you have to have a self parameter.
If you want to look up the players by name, then do that. Don't have a list of players, but instead a dict of players.
2) vars() is something you should almost never be using. It is used so that you can pretend that a string is a variable name. You do not need to do this for anything that you're doing here. In fact, you do not need a variable at all in most of the places where you're using one. You have more to learn about than just OO here.
Consider this part for example:
vars()[team_name] = team(team_name)
list_of_teams.append(team_name)
Instead of trying to remember the team by name in vars(), again, look up the teams by name. Have a dict of teams instead of a list. To get the names of teams, you can just print the keys of the dictionary.
Simple is better than complicated. Creating variables on the fly is complicated. Using dictionaries is simple.
I hate spoon-feeding this much code, but it seems like the only way to get the idea(s - I didn't really say everything above) across this time:
# Just like we want a class to represent teams, since those are "a thing" in our
# program, we want one for each player as well.
class player(object):
__slots__ = ['name', 'stats', 'pos']
def __init__(self, name, stats, pos):
self.name = name
self.stats = stats
self.pos = pos
# Asking the user for information to create an object is not the responsibility of
# that class. We should use external functions for this.
def create_player(name):
print 'add stats' # fill out the appropriate stats through raw_input
stat1 = raw_input('stat1: ')
stat2 = raw_input('stat2: ')
pos = raw_input('POS: ')
# Now we create and return the 'player' object.
return player(name, {'stat1': stat1, 'stat2': stat2}, pos)
class team(object):
__slots__ = ['name_to_player', 'position_to_player']
def __init__(self):
# We don't make any lists, just dicts, because we want to use them primarily
# for lookup. Notice how I've named the attributes. In particular, I **don't**
# talk about type names. That's just an implementation detail. What we care about
# is how they work: you put a name in, get a player out.
self.name_to_player = {}
self.position_to_player = {}
# Again, we don't ask the questions here; this just actually adds the player.
def add_player(self, player):
self.name_to_player[player.name] = player
self.position_to_player[player.pos] = player
# Again, we don't ask the questions here; this just does the actual edit.
def edit_player(self, name, stat, new_value):
self.name_to_player[name].stats[stat] = new_value
def main(): # the main function
teams = {} # dict from team name to team object.
while True:
print teams.keys()
# Your human interface was needlessly awkward here; you know from the supplied name
# whether it's a new team or an old one, because it will or won't be in your
# existing set of teams. Similarly for players.
team_name = raw_input('team name? ')
if team_name not in teams.keys():
teams[team_name] = team() # create a new team
else: # edit an existing one
team = teams[team_name]
player_name = raw_input('player name? ')
if player_name in team.name_to_player.keys(): # edit an existing player
stat = raw_input("stat? ")
value = raw_input("value? ")
team.edit_player(player_name, stat, value)
else: # add a new player
team.add_player(create_player(player_name))
if __name__ == '__main__':
main()
This still isn't doing everything "right", but it should give you more than enough to think about for now.
First of all, the traceback that accompanies the Key error, will tell you which line in your program triggered it, and if it is not obvious from reviewing the code, then inserting a print statement before that line should make it obvious.
Second, you are using user input as a key. User input is not reliable. You WILL have key errors all the time so your code should be dealing with that, either by using try: except: to catch the exception, or by checking every time using if key in mydict: before actually using the key to lookup the dictionary.
Third, what you are doing with vars() is very, very weird. If your app uses a global variable, then it should know the name and have no need to refer to vars. Have you forgotten to declare a global variable in some method?
def method(self,name):
global bigdict
bigdict[name] = "set at least one time"

Categories

Resources