Python Count function not work on instances of a class? - python

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.

Related

Why can't I add elements to this list? (Object oriented programming in python)

Hi everyone I am currently creating a student class to record down exam results and find the average score. Here's the code I have right now:
class Student:
def __init__(self, name):
self.name = name
self.all_grades = []
def add_exam(self, newest_grade):
self.newest_grade = newest_grade
self.all_grades = self.all_grades.append(newest_grade)
def get_mean(self):
self.average = sum(self.all_grades)/len(self.all_grades)
Josh = Student()
Josh.add_exam(72)
However, when I try to put print(Josh.all_grades), it doesn't return anything and type(Josh.all_grades) returns a none type.
I am really lost and I don't understand why it doesn't return [72]
You've to just type self.all_grades.append(newest_grade) instead of typing self.all_grades=self.all_grades.append(newest_grade). When you type append(), it adds element to list. But it returns None. So when you type <list_variable>.append(<element>) it adds the element, but when you type <list_variable>=<list_variable>.append(<element>) then the value of whole list is now None

List Manipulation Based on Class Variables

Apologies if I explain something wrong or use the wrong wording, my programmer vocabulary isn't the best. If anyone understands my problem and has better ways of explaining it feel free to do so. I have a problem similar to a problem here. I want to remove items from a list that occur in another list. But one list will have strings that reference the variable "name" within class objects.
class sword:
name = 'swordName'
class bow:
name = 'bowName'
class axe:
name = 'axeName'
inventory = [sword, bow, sword, axe]
select = ['bowName', 'swordName']
I want to be able to create a list "selectedItems" with the class objects out of inventory based off of the strings in "select" that are equal to the "name" of the class objects. It also needs to work if "inventory" and "select" both have duplicates in them.
Output:
>> inventory = [bow, axe]
>> selectedItems = [bow, sword]
One other thing I would like the program to ignore if there are more "name"s in select than there are corresponding class objects in "inventory", and to ignore if a string in "select" has no corresponding class objects.
For example, if "inventory" is [sword, axe] and "select" is ['bowName', 'non-existent', 'axeName'], the result is that "inventory" is [sword] and "selectedItems" is [axe].
A simple way of explaining this is that select will take from inventory, but if select can't take from inventory nothing happens.
You may make base class with magic methods __eq__ and __hash__ which can allow you to manage comparing your objects as you want:
class BaseItem:
name = None
def __init__(self):
self.__name = self.name
def __eq__(self, other):
return self.__name == other
def __hash__(self):
return id(self.__name)
def __repr__(self):
return f"'{self.__name}'"
class Sword(BaseItem):
name = "swordName"
class Bow(BaseItem):
name = "bowName"
class Axe(BaseItem):
name = "axeName"
inventory = [Sword(), Bow()]
select = ["swordName", "bowName", "axeName", "swordName", "bowName"]
# casting lists into sets and getting difference between them
result = set(inventory) - set(select)
print(result) # output {'swordName', 'bowName'}
eq - actually is unused here but i added that you can compare your objects with strings, lists etc:
Sword() in ["swordName"] # true
Sword() in ["bowName"] # false
Sword() == "swordName" # true
Sword() == "bowName" # false
hash - need to comparing two objects, actually it use for getting difference between two sets
repr - it is not really required method, it needs just for pretty displaying of objects
selectedItems = list()
# make a new list of the names of the objects in the inventory
# inventory and inventory names have the same index for the same item
inventory_names = [x.name for x in inventory]
for s in select:
if s in inventory_names:
index = inventory_names.index(s)
inventory_names.pop(index)
selectedItems.append(inventory.pop(index))

How can I check if an attribute of a class exists in array?

I want to know if it is possible to check if an array of a class has a specific instance of an attribute, and return True if it does. In other words, does an instance of a class attribute exist in an array of that class?
In my example, I have an array of class Team. The class Team has an attribute, name. I want to check if a Team instance with a specific name exists by iterating over an array of Team instances.
Class Team:
class Team:
def __init__(self):
self.name = name # (String)
[Invalid] This is the how I wanted to write the function:
# team_name is a String variable
# teams is an array of the Team class
def team_name_taken(team_name, teams):
if team_name in teams.name:
return True
else:
return False
I know this doesn't work, but is it possible to iterate over the same attribute within an array in this fashion?
[Valid] Regarding the goal of my code, I have the following code that works properly:
def team_name_taken(team_name, teams):
for team in teams:
if team_name == team.name:
return True
return False
I know that this works, I was just wondering if there was another way to do it, similar to the invalid way I represented above.
What you could do is the following:
def team_name_taken(team_name, teams):
team_names = [team.name for team in teams]
if team_name in team_names:
return True
else:
return False
This will generate a list with the items of the list being all team names. Once you have done that you can do if team_name in team_names: and get the desired outcome.
If on the other hand you just want to make it a oneliner you could do this:
def team_name_taken(team_name, teams):
return len([ team for team in teams if team.name == team_name ]) >= 1
This will make a list then check if the len() of the list is 1 (or bigger) and if that is the case return True else False.
The following class definition allows name from the user to be assigned to the Team.name attribute and forces it to be a str object:
class Team(object):
def __init__(self, name):
self.name = str(name)
Here is an example list of Team instances:
teams = [Team(x) for x in range(10)]
You can do what I think you want with some one-liners:
Using another list comprehension to find out if any of the names equal '3':
any([team.name == '3' for team in teams])
Or if you really want to use a function you can use the following:
any(map(lambda obj: obj.name == '3', teams))

Accessing method variables of one class in another python class

I have started learning Object Oriented concepts in python. I have got this sample code below:
class Student(object):
def __init__(self, name, age):
self.name = name
self.age = age
class School(object):
students = []
def __init__(self, name):
self.name = name
def add_student(self, student):
self.students.append(student)
def show_students(self):
print("{0} Student Roster".format(self.name))
for s in self.students:
print("{0}: {1}".format(s.name, s.age))
my_school = School("Quora University")
first_student = Student("Rongan Li", 20)
second_student = Student("Jason Kane", 20)
my_school.add_student(first_student)
my_school.add_student(second_student)
my_school.show_students()
Question:
In the def show_students method of School class how they are accessing the construct variables of class student without instance of that class?
first_student = Student("Rongan Li", 20)
second_student = Student("Jason Kane", 20)
These lines create object of student class ,first_student which has name 'Rongan Li' and age '20'.
second_student which has name 'Jason Kane' and age '20'
Now, you add these 2 objects to the list.
my_school.add_student(first_student)
my_school.add_student(second_student)
Now, when you iterate in the list
for s in self.students:
print("{0}: {1}".format(s.name, s.age))
's' goes to first element of list, and since it is an object of class Student , 's' becomes object of student class. Now, since 's' is first element in the list and on first place you added 'Rongal Li' and 20 . So, s has 2 properties name and age. When you do s.name , it prints 'Rongal Li' and s.age prints 20
Then it goes to next element and same process is repeated.
in the method show_students there's a for loop :
for s in self.students:
print("{0}: {1}".format(s.name, s.age))
it loops over the list of students added by method add so inside the loop the variable "s" represent an instance of student class so s.name is a legit way of accessing an instance variable by using an instance of the class
The students are added to School.students list via the add_student method.
my_school.add_student(first_student)
my_school.add_student(second_student)
These two lines add two references to the School.students list in my_school. So now the list has two references pointing at first_student and second_student.
You can think of the list in my_school at this point as
students = [Pointer to first_student, Pointer to second_student]
When you call my_school.show_students(), my_school.students list (the list above) is accessed and through the references to first_student and second_student in the list, you access the original first_student object and second_student object and can then retrieve their properties.

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