Class does not seem to be Global in python - python

I setup a class and it accepts and prints out the variables fine in one if statement.
class npc: #class for creating mooks
def __init__(self, name):
self.name = name
def npc_iq (self,iq):
self.iq = []
def npc_pp (self,pp):
self.pp = []
def npc_melee (self, melee):
self.melee = []
def npc_ct (self, ct):
self.ct = []
It works fine in this if statement
if menu_option == 1:
print "Choose melees for npc"
init_bonus = random.randint(0,2)
char_PP = random.randint(7,15)
char_iq = random.randint(7,15)
npc_Melees = int(raw_input(prompt))
combat_time = math.floor((round_attacks - init_bonus - math.floor(char_PP/2) - math.floor(char_iq/2)) / npc_Melees)
#function for calculating sequence number
print "combat time is"
print combat_time
mook = "mook%s" % counter # adds different mook names to program
mook = npc(mook)
mook.iq = (char_iq)
mook.pp = (char_PP)
mook.melee = (npc_Melees)
mook.ct = (combat_time)
counter += 1
But on this statement it will print out the name in the class but not ct.
elif menu_option ==4:
print "Printing out all mooks"
print
printcount = counter -1
while printcount != 0:
mookprint = "mook%s" % printcount
mookprint = npc(mookprint)
print mookprint.name
print mookprint.ct
print
printcount -= 1

Why would a mookprint have any idea what value ct should be? The constructor for npc initialises a new instance of npc, with the name given as a parameter, but ct is left empty.
When you create an NPC in menu option 1, you do not create a global instance of npc. If you want to refer to a previously created instance of npc, you will need to find some way of storing them. Dictionaries may be a good solution for you. A dictionary is an object that holds mappings between keys and values. If you know the key, then you can find the assosicated value. In this case you would make name the key and the value the npc instances.
eg.
npcsDict = dict()
if menu_option == 1:
# code for intialising a new instance of npc
...
# most, if not all of the initialisation code should be moved to the
# __init__ method for npc
# now store the newly created mook
npcsDict[mook.name] = mook
elif menu_option == 4:
print "Printing out all mooks"
print
for mookName in npcsDict:
print npcsDict[mookName].name
print npcsDict[mookName].ct
print

i dont really understand your problem.
your working example:
mook = npc(mook)
mook.iq = (char_iq)
mook.pp = (char_PP)
mook.melee = (npc_Melees)
mook.ct = (combat_time)
mook.ct is value of (combat_time)
your failing example:
mookprint = npc(mookprint)
print mookprint.name
print mookprint.ct
mookprint.ct's value is nothing because it is never set.

The elif will only be executed if the if has not, so if the elif block runs, ct was never set

I don't think you're understanding how four lines work:
mookprint = "mook%s" % printcount
mookprint = npc(mookprint)
print mookprint.name
print mookprint.ct
Every time this block of code is run, the following things are happending:
You're assigning a string of the form "mook" to the variable mookprint
You're creating a new instance of the npc class. You should note that all of the instances you're creating will be separate from eachother. This new instance will have an attribute with the name that was previously held in the variable mookprint and this instance of npc will be assigned to mookprint.
You're printing the name attribute of the instance of the npc class that you created in the previous step. This works because when this instance was created, the __init__ method of your class was called with the argument name being set to "mook1" or whatever was stored in mookprint at the time.
You're printing the ct attribute of the instance of the npc class that you just created. Since you never set the ct attribute to anything, this will not work how you expected.
If you want to count the number of instances of your npc class, you'll need to create a class attribute. This is a variable whose value is common across all instances of a class. To do so, you'll need to modify your class definition to add an item to this attribute every time you make a new instance of the class. It will look something like this:
class npc: #class for creating mooks
ct = []
def __init__(self, name):
self.name = name
self.ct.append(name)
def get_ct(self):
return len(self.ct)
With the above, the variable ct will be a list that is common to all instances of npc and will grow every time a new npc is created. Then the method get_ct will count how long this list is.
Then you'll need to modify the four lines I mentioned to look like:
mookprint = "mook%s" % printcount
mookprint = npc(mookprint)
print mookprint.name
print mookprint.get_ct()
I think the code above shows how to change your code to work more how you expected it to work. However, it should be noted that you rarely want to create classes where each instance depends on information about the other instances. It is usually a better design to do something like Dunes suggested, storing the instances in a dictionary, or some other data structure, and keeping track of them that way.

Related

print the value of a NumericProperty() kivy

If I have a Numeric Property in two of my screens that count how many times a person clicked the correct icon and I want to print it in a separate class how would I do this? I have tried using the print() function and using print(StringProperty(str())) but I dont get a number value printed. When I print the correct_counter in VerifyAA() and VerifyBB() the correct value is printed.
class A(Screen):
correct_counter1 = NumericProperty(0)
def verifyAA(self, *args):
if self.AA == V.Check1 or self.AA == V.Check2 or self.AA == V.Check3:
print("You got it!!!")
self.correct_counter1 = self.correct_counter1 + 1
print(self.correct_counter1)
self.ids.aa.disabled = True
class B(Screen):
correct_counter2 = NumericProperty(0)
def verifyBB(self, *args):
if self.BB == VV.Check1 or self.BB == VV.Check2 or self.BB == VV.Check3:
print("You got it!!!")
self.correct_counter2 = self.correct_counter2 + 1
print(self.correct_counter2)
self.ids.bb.disabled = True
class Answers(Screen):
print(A.correct_counter1)
print(StringProperty(str(B.correct_counter2)))
This is what gets printed respectively:
<NumericProperty name=correct_counter>
<StringProperty name=>
You need to understand the distinction between the definition of a class and an instance of a class. When you print A.correct_counter1 you print the NumericProperty object itself, defined at class level. That's the only sensible result - it wouldn't make sense for it to print the value of the property, because it doesn't have a value. For instance, if you wrote instance1 = A(correct_counter1=1); instance2 = A(correct_counter1=20) what value would you have expected print(A.correct_counter1) to print? The answer has to be neither, the value of the property is defined in each case for the class instance, not the class itself.
The correct solution is that you must print the value of the property via an instance of your class. For instance, if you write instance = A(); print(instance.correct_counter1) you will print a number like you expect. The best way to do this depends on the structure of your program and the relationship between the classes. Your example isn't a runnable program so it isn't possible to make a specific suggestion. If you have an actual example where you want to do this but can't work out how, post that as a new question.

Python Accessing Dict That Has Defualt Values That Have Been Updated

So I have this class:
class hero():
def __init__(self, name="Jimmy", prof="Warrior", weapon="Sword"):
"""Constructor for hero"""
self.name = name
self.prof = prof
self.weapon = weapon
self.herodict = {
"Name": self.name,
"Class": self.prof,
"Weapon": self.weapon
}
self.herotext = {
"Welcome": "Greetings, hero. What is thine name? ",
"AskClass": "A fine name, {Name}. What is your class? ",
"AskWeapon": "A {Class}, hmm? What shalt thy weapon be? ",
}
def setHeroDicts(self, textkey, herokey):
n = raw_input(self.herotext[textkey].format(**self.herodict))
if n == "":
n = self.herodict[herokey]
self.herodict[herokey] = n
#print self.herodict[herokey]
def heroMake(self):
h = hero()
h.setHeroDicts("Welcome", "Name")
h.setHeroDicts("AskClass", "Class")
h.setHeroDicts("AskWeapon", "Weapon")
And in another class I have this executing
def Someclass(self):
h = hero()
print h.herodict["Class"]
h.heroMake()
print h.getClass()
if "Mage" in h.herodict["Class"]:
print "OMG MAGE"
elif "Warrior" in h.herodict["Class"]:
print "Warrior!"
else:
print "NONE"
So if I input nothing each time, it will result in a blank user input, and give the default values. But if I put an input, then it will change the herodict values to what I customize. My problem is, if I try and access those updated values in Someclass it only gives me the default values instead of the new ones. How do I go about accessing the updated values?
The main issue with your class is that you are creating a new object within heromake instead of using the existing one. You can fix this by replacing h with self (so that each time you are calling setHeroDicts on the object):
def heromake(self):
self.setHeroDicts("Welcome", "Name")
self.setHeroDicts("AskClass", "Class")
self.setHeroDicts("AskWeapon", "Weapon")
The first argument to a method is always set to the instance itself, so if you want to interact with the instance or mutate it, you need to use it directly. When you do h = hero() in your original code, you create a whole new hero object, manipulate it and then it disappears when control passes back to your function.
A few other notes: you should name your classes with CamelCase, so it's easier to tell they are classes (e.g., you should really have class Hero) and in python 2, you need to make your classes descend from object (so class Hero(object)). Finally, you are duplicating nearly the entire point of having classes with your herodict, you should consider accessing the attributes of the object directly, instead of having the intermediary herodict (e.g., instead of doing h.herodict["Class"] you could do h.prof directly.

Global Name Not Defined in Python

Am new to Python OOP. Please dont be harsh. Here is my code which calculates which is the fastest time of an athlete from a list and displays them. But When Running, I get this error:
z= add.mylist.min()
NameError: global name 'add' is not defined
My Code:
class Athlete:
def add(self):
list=[]
mylist=[]
for i in range(2):
self.name = raw_input("Enter name: ")
self.fastest_time = input("time: ")
list.append(self.name)
mylist.append(self.fastest_time)
print "Names: ",list
print "Fastest times: ",mylist
def display(self):
z= add.mylist.min()
w= add.mylist.index(z)
print "Minimum time: ",z
print "Name of athelte with fastest time: ",list[w]
x = Athlete()
x.add()
x.display()
You need to refer to methods on the instance with the self parameter. In addition, your add() method needs to return the mylist variable it generates, you cannot refer to method local variables as attributes on methods:
def display(self):
mylist = self.add()
z = min(mylist)
w = mylist.index(z)
def add(self):
list=[]
mylist=[]
for i in range(2):
self.name = raw_input("Enter name: ")
self.fastest_time = input("time: ")
list.append(self.name)
mylist.append(self.fastest_time)
print "Names: ",list
print "Fastest times: ",mylist
return mylist
That is what self is for, as a reference point to find instance attributes and other methods on the same object.
You may want to rename list to something that does not shadow the built-in type.
Martijn has already answered your question, so here are some remarks and code style tips:
New-style classes derive from object
You have both athlete names and their times, those belong together as key-value pairs in a dictionary instead of two separate lists
Don't use print statements inside class methods, a class method should return an object that you then can print
what if you have more then 2 athletes for which you want to enter the time? If you make the number of athletes an argument of your function, you can add a variable number of athlethes
give descriptive variable names (not mylist) and don't use names of builtin functions (like list) as variable name
variables that you want to use throughout your class can be initalized in an __init__method.
For printing, use the format function instead of using commas
use if __name__ == '__main__' so that your Python file can act as either reusable modules or as standalone program
Taking these into account, I would rewrite your code to something like this:
from collections import defaultdict
class Athlete(object): # see (1)
def __init__(self): # see (6)
self.athlete_times = defaultdict(str) # see (2)
def add_athletes_and_times(self, n): # see (4)
for i in range(n):
self.name = raw_input("Enter name: ")
self.fastest_time = input("time (in seconds): ")
self.athlete_times[self.fastest_time] = self.name
def get_fastest_time(self):
return min(self.athlete_times) # see (3)
if __name__ == '__main__': # see (8)
x = Athlete()
x.add_athletes_and_times(2)
for fast_time in x.athlete_times:
print "The fastest time of athlete {0} is {1} seconds.".format(
x.athlete_times[fast_time], fast_time) # see (7)
fastest_time = x.get_fastest_time()
print "The overall fastest time is: {0} seconds for athlete {1}.".format(
fastest_time, x.athlete_times[fastest_time])

Get the return value from a function in a class in Python

I am trying to simply get the value out of my class using a simple function with a return value, I'm sure its a trivial error, but im pretty new to python
I have a simply class set up like this:
class score():
#initialize the score info
def __init__(self):
self.score = 0
self.num_enemies = 5
self.num_lives = 3
# Score Info
def setScore(num):
self.score = num
# Enemy Info
def getEnemies():
return self.num_enemies
# Lives Info
def getLives():
return self.getLives
etc.....
Than I create an instance of the class as such:
scoreObj = score()
for enemies in range(0, scoreObj.getEnemies):
enemy_sprite.add(enemy())
I get the error saying that an integer is expected, but it got an instancemethod
What is the correct way to get this information?
Thanks!
scoreObj.getEnemies is a reference to the method. If you want to call it you need parentheses: scoreObj.getEnemies().
You should think about why you are using a method for this instead of just reading self.num_enemies directly. There is no need for trivial getter/setter methods like this in Python.
The first parameter for a member function in python is a reference back to the Object.
Traditionally you call it "self", but no matter what you call the first parameter, it refers back to the "self" object:
Anytime I get weird errors about the type of a parameter in python, I check to see if I forgot the self param. Been bit by this bug a few times.
class score():
#initialize the score info
def __init__(self):
self.score = 0
self.num_enemies = 5
self.num_lives = 3
# Score Info
def setScore(self, num):
self.score = num
# Enemy Info
def getEnemies(self):
return self.num_enemies
# Lives Info
def getLives(foo): #foo is still the same object as self!!
return foo.num_lives
#Works but don't do this because it is confusing
This code works:
class score():
def __init__(self):
self.score = 0
self.num_enemies = 5
self.num_lives = 3
def setScore(self, num):
self.score = num
def getEnemies(self):
return self.num_enemies
def getLives(self):
return self.getLives
scoreObj = score()
for enemy_num in range(0, scoreObj.getEnemies()):
print enemy_num
# I don't know what enemy_sprite is, but
# I commented it out and just print the enemy_num result.
# enemy_sprite.add(enemy())
Lesson Learned:
Class functions must always take one parameter, self.
That's because when you call a function within the class, you always call it with the class name as the calling object, such as:
scoreObj = score()
scoreObj.getEnemies()
Where x is the class object, which will be passed to getEnemies() as the root object, meaning the first parameter sent to the class.
Secondly, when calling functions within a class (or at all), always end with () since that's the definition of calling something in Python.
Then, ask yourself, "Why am I not fetching 'scoreObj.num_lives' just like so instead? Am I saving processing power?" Do as you choose, but it would go faster if you get the values directly from the class object, unless you want to calculate stuff at the same time. Then your logic makes perfect sense!
You made a simple mistake:
scoreObj.getEnemies()
getEnemies is a function, so call it like any other function scoreObj.getEnemies()

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