Looping through attributes within Class instances? - python

I am trying to simulate a baseball game to learn more about python and programming in general... I ran into an interesting learning point in programing... and was wondering if someone could explain this error...
import random
rosterHome = []
rosterAway = []
class Player:
def __init__(self, number, battingAverage):
self.number = number
self.battingAverage = battingAverage
class Game:
def __init__(self):
self.inning = 0
self.homeScore = 0
self.awayScore = 0
self.outs = 0
def createStats():
for i in range(40):
stats = random.random()
x = Player(i, stats)
rosterHome.append(x)
for y in range(40):
stats = random.random()
y = Player(i, stats)
rosterAway.append(y)
def startGame():
Game.createStats()
Game.inning = 0
Game.homeScore = 0
Game.awayScore = 0
Game.outs = 0
Game.playInning()
def playInning():
totalHits = 0
if Game.inning >= 10:
print('Game is Over')
return
while Game.outs < 3:
for i in rosterHome:
x = rosterHome[i]
if x.battingAverage > random.random():
totalHits += 1
player += 1
print('batter ', player, ' got a hit')
else:
Game.outs += 1
player += 1
print('batter ', player, ' got out')
print('there are ', Game.outs, ' outs.')
Game.startGame()
x = rosterHome[i]
TypeError: list indices must be integers or slices, not Player

TLDR:
List indices must be integers or slices
The interpreter says "Hey, I see you're trying to access an item in a List by its index, but indices should be of type integer, however, you passed a value of type Player"
In Python and most programming languages, to reference an item in a List/Array, one way would be by index. Lists are zero-indexed, so the first item is of index 0, the second index 1, and so on.
Given an Array
my_array = ["bread", "foo", "bar"]
my_array[0] # would give you "bread"
my_array[1] # would give you "foo"
my_array[2] # would give you "bar"
However in your case, if we trace back up from where the error occurred, right here:
x = rosterHome[i]
You want to ask, what is the value of i? above this line is a for loop, and i represents each value in a list called rosterHome. So what the heck is in rosterHome anyways?
Moving up into your createStats method where you populated the rosterHome list, we see that you're pushing an instance of Player into the rosterHome list.
x = Player(i, stats)
rosterHome.append(x)
So rosterHome really isn't a list of numbers but instead a list of Player instances. You might want to review and try again, maybe accessing the number property of the Player object instead.

The error happens because rosterHome is a list of instances of the Player class, so when you iterate on the list (for i in rosterHome) each element will be an instance of said class (i is a Player). If you want to access the number of each player you'll have to access the attribute number of your Player instances, but it seems like actually you want to find the player instance. This means, you don't even need to lookup the value in the table, just use the value of the for loop. I'll use a different naming of variables to improve readability:
while Game.outs < 3:
for player in rosterHome:
# x wanted to access a player, but we don't need to do that actually
if player.battingAverage > random.random():
# ...
else:
# ...
This part of the answer considers that you actually want to meet both requirements (number of outs and iterate players once):
player_index = 0
while Game.outs < 3 and player_index< len(rosterHome):
player = rosterHome[player_index]
if player.battingAverage > random.random():
# ...
else:
# ...
if Game.outs == 3:
# Reached 3 outs
else:
# No players left and game outs < 3

Related

I am unable to fetch an element from a list of dictionaries [duplicate]

This question already has answers here:
How can I use `return` to get back multiple values from a loop? Can I put them in a list?
(2 answers)
Closed 8 months ago.
I have been working on this assignment for school and I just can't figure out what why I cant get this program to work properly. I am trying to get the program to allow the user to enter three animals. It is only allowing me to enter one. I know it has to do with my placement of the return statement in the make_list function but can't figure out how to fix it.
Here is my code:
import pet_class
#The make_list function gets data from the user for three pets. The function
# returns a list of pet objects containing the data.
def make_list():
#create empty list.
pet_list = []
#Add three pet objects to the list.
print 'Enter data for three pets.'
for count in range (1, 4):
#get the pet data.
print 'Pet number ' + str(count) + ':'
name = raw_input('Enter the pet name:')
animal = raw_input('Enter the pet animal type:')
age = raw_input('Enter the pet age:')
#create a new pet object in memory and assign it
#to the pet variable
pet = pet_class.PetName(name,animal,age)
#Add the object to the list.
pet_list.append(pet)
#Return the list
return pet_list
pets = make_list()
Your problem is, precisely, that you're putting the return statement inside the for-loop. The for-loop runs each statement in it for however so many times.. if one of your statements is a return, then the function will return when it hits it. This makes sense in, for example, the following case:
def get_index(needle, haystack):
for x in range(len(haystack)):
if haystack[x] == needle:
return x
Here, the function iterates until it finds where the needle is in the haystack, and then returns that index (though there's a builtin function to do this, anyways, list.index()).
If you want the function to run for however many times you tell it to, you have to put the return AFTER the for-loop, not inside it. That way, the function will return after the control gets off the loop
def add(numbers):
ret = 0
for x in numbers:
ret = ret + x
return ret
(though again, there's a builtin function to do this as well, sum())
You just need to return the pet_list outside of the for loop, so it will happen after the loop has finished running.
def make_list():
pet_list = []
print 'Enter data for three pets.'
for count in range (1, 4):
print 'Pet number ' + str(count) + ':'
name = raw_input('Enter the pet name:')
animal=raw_input('Enter the pet animal type:')
age=raw_input('Enter the pet age:')
print
pet = pet_class.PetName(name,animal,age)
pet_list.append(pet)
return pet_list
You have the return statement at the incorrect level of indentation. It should be at the same depth as the for statement. Having the return within the loop causes it to break out of the loop.
Remove one indent before the return.
You notice the for loop runs only once because the return statement is inside the if statement, within the loop
I've had a similar problem right now with my code:
Return the number of even ints in the given array. Note: the % "mod" operator computes the remainder, e.g. 5 % 2 is 1.
count_evens([2, 1, 2, 3, 4]) → 3
count_evens([2, 2, 0]) → 3
count_evens([1, 3, 5]) → 0
def count_evens(nums):
summary = 0
for i in nums:
if i % 2 == 0:
summary += 1
return summary
count_evens([2, 1, 2, 3, 4])
if you go to visualize execution and paste in my code http://www.pythontutor.com/visualize.html#mode=edit
Once I unindented it 8 spaces (same depth as the for statement), it ran multiple times and gave the correct output.
Your spacing is off. return pet_list is in the scope of the for loop.

Python, "If if line is called on first time, do something else"

So the title is pretty self explanatory, but i'll go into more detail. I am creating a text dependent game and I will have millions of areas. And each time you enter a new area, you will be greeted with a one time only different reaction than if you came into the same place again later, and I need to find a way to to this:
if len(line) == 1:
do exclusive thing
else:
do normal thing
Sure, I could use a counter system like "a = 0" but then I would need to create a separate counter for every single area I create, and I don't want that.
You could just store a single dict to keep track of room visits, and probably even better to use a defaultdict
from collections import defaultdict
#Using a defaultdict means any key will default to 0
room_visits = defaultdict(int)
#Lets pretend you had previously visited the hallway, kitchen, and bedroom once each
room_visits['hallway'] += 1
room_visits['kitchen'] += 1
room_visits['bedroom'] += 1
#Now you find yourself in the kitchen again
current_room = 'kitchen'
#current_room = 'basement' #<-- uncomment this to try going to the basement next
#This could be the new logic:
if room_visits[current_room] == 0: #first time visiting the current room
print('It is my first time in the',current_room)
else:
print('I have been in the',current_room,room_visits[current_room],'time(s) before')
room_visits[current_room] += 1 #<-- then increment visits to this room
You need static var : What is the Python equivalent of static variables inside a function?
def static_var(varname, value):
def decorate(func):
setattr(func, varname, value)
return func
return decorate
#static_var("counter", 0)
def is_first_time():
is_first_time.counter += 1
return is_first_time.counter == 1
print(is_first_time())
print(is_first_time())
print(is_first_time())

Random List Generation not working

I am working on a game where I need to randomly generate classes for a list. I use a self-made function randList to do this. The code for that function looks like this:
def randList(options, num): #RANDOMLY SELECTS NUM ITEMS FROM LIST OPTIONS
returnVal = [] #CREATE A LIST THAT IT RETURNS
for i in range(num - 1): #FOR DESIRED NUMBER OF RETURN ITEMS
val = r.choice(options) #RANDOMLY SELECT ITEM FROM OPTIONS
returnVal.append(val) #ADD THAT TO RETURNVAL
options.remove(val) #REMOVE IT FROM OPTIONS.
return returnVal #RETURN GENERATED LIST
I am using that to randomly generate monsters and items in a room like so:
class roomParent: #ROOM CHARACTER FINDS
def __init__(self, entities, floor): #INIT WITH ENEMIES IN ROOM, ITEMS ON FLOOR
self.entities = entities #ENEMIES THERE ARE
self.floor = floor #ON FLOOR THERE IS
def generate(self):
global enemiesBeat
if enemiesBeat >= 500:
self.entities = [dragon]
else:
self.entities = randList([goblin, dwarf, slime, naga, troll, beholder], 1)
self.floor = randList([scrap, scrap, scrap, fireJar, ambrosia, sword, spearhead, armor, potion, slimeball], r.randint(0, 3))
room = roomParent([], [])
Just so you know, goblin, dwarf, slimeball, etc. are defined earlier in the code. I don't think they have anything to do with the problem. I generate the room later like this:
def main():
room.generate()
print("Enemies: " + str(room.entities))
main()
I want it to print out a list with two random monsters in it from room.generate(), but it always prints Enemies: []. There are no errors in the code, and after trying to troubleshoot for 10 minutes, I decided to consult he web with no fruits in result of that labor. Thank you in advance for any help you give.
As Oliver points out, the reason you get always get an empty entities array is because self.entities is set to randList([goblin, dwarf, slime, naga, troll, beholder], 1) within generate (I assume the global variable enemiesBeat is less than 500 in your tests).
In your randList function you have an off-by-one error that I mention in the comments which means that the generated list will contain one fewer items than specified by num. As you try to generate a singleton list for self.entities (num = 1), you'll actually have it assigned to an empty list.
You can correct this issue by changing for i in range(num - 1) to for i in range(num) in your randList function.
As an aside, I don't think you need to pass entities and floor as parameters to the roomParent constructor, since it doesn't seem to have any effect. Instead, you could modify the class definition:
class roomParent(object):
def __init__(self):
self.entities = []
self.floor = []
...
And instantiate it like this:
room = roomParent()
Use the random.sample library function.
Also, you might want to rethink your capitalization...snake_case is preferred over inCaps for function names.

How to properly create a list subclass that returns a list generated from file in python?

I am running into some problems with subclassing.
I need to create a class that inherits list properties but I need the returned list (self.tiles_pool) to be one that is created when the class is constructed. The list is generated by an external CSV file. I tried creating the class without inheritance but quickly realized there was no way to return my list without creating a separate method to do so. But I need the instance of the class to be a list object (i.e. print the generated list when I print the object). The class is for creating a Tile Pool like the bag of letter tiles in scrabble.
If there is some way of returning my list (self.tiles_pool) without inheritance and without using user defined methods that would be better.
Here is my code so far:
import csv
import random
class TilePool(list):
def __init__(self):
list.__init__(self)
# Open csv file
with open("tiles.csv") as f:
# Read csv into list of lists of each lines values
tiles_csv = csv.reader(f, delimiter=",")
# Convert the list into a tile pool
self.tiles_pool = [(line[0], line[1])
for line in tiles_csv if len(line[0]) == 1
for x in xrange(int(line[2]))]
del tiles_csv
def pop(self, tile_count=None):
assert len(self.tiles_pool) > 0, "# Tile Pool is empty"
if tile_count is None:
tile_count = 7
assert tile_count in xrange(1, 8), "# Tile Count must be between 1 and 7"
new_tiles = []
counter = 1
while len(self.tiles_pool) > 0 and counter <= tile_count:
rand_choice = random.choice(self.tiles_pool) # Get a random tile
new_tiles.append(rand_choice) # Add it to new_tiles list
self.tiles_pool.remove(rand_choice) # Delete it from pool
counter += 1
return new_tiles
def view_pool(self):
if len(self.tiles_pool) == 0:
print("# Tile Pool is empty")
else:
for tile in self.tiles_pool:
print("{letter}: {score}".format(letter=tile[0], score=tile[1]))
print len(self.tiles_pool)
I understand that this implementation may seem strange and I could probably just put this in a function but I am under instruction to make it a class. Any help or advice would be much appreciated. Thanks.
Why subclass list if you don't use it? self is the list you want, there is no need to create a tiles_pool. Since lists can be initialized with a sequence, delay parent __init__ until you can feed it the csv. Your code does some things I don't understand such as duplicating tiles line[2] times, but I figure you know what you are doing there.
In this sample, I remove tiles_pool in favor of using self and do a few other tweaks like using the "truthiness" of lists (bool([1]) is True while bool([]) is False) instead of using len but it should work the same as the original.
import csv
import random
class TilePool(list):
def __init__(self):
with open("tiles.csv") as f:
tiles_csv = csv.reader(f, delimiter=",")
list.__init__(self, (line[0:2]
for line in tiles_csv if len(line[0]) == 1
for x in xrange(int(line[2]))))
def pop(self, tile_count=None):
# non-empty list is truthy
assert self, "# Tile Pool is empty"
if tile_count is None:
tile_count = 7
assert tile_count in range(1, 8), "# Tile Count must be between 1 and 7"
new_tiles = []
counter = 1
while self and counter <= tile_count:
rand_choice = random.choice(self) # Get a random tile
new_tiles.append(rand_choice) # Add it to new_tiles list
self.remove(rand_choice) # Delete it from pool
counter += 1
return new_tiles
def view_pool(self):
if not self:
print("# Tile Pool is empty")
else:
for tile in self:
print("{letter}: {score}".format(letter=tile[0], score=tile[1]))
print len(self)
t = TilePool()
t.pop(3)
t.view_pool()
If you want to avoid inheritance and user-defined methods, I'd leverage python's magic methods to do so.

how to make variable = the value of a function call?

We have to use a previously made function (the mega_calculator) to calculate the average amount of property damage for 10 buildings. however, we need to find out which building is going to be the most destroyed, but we keep getting error messages about comparing functions to ints. for some reason, the y variable(used to store mega_calculator values) is being labeled as a function, and the if statements aren't being triggered.
We are trying to use a for loop but it doesn't change anything. We also tried asserting inside mega_calculator that the return value must be an integer type, but that didn't do anything. we tried saving the average value as a variable and asserting that as an integer type but that didn't do anything.
What should I do for that?
Any help is loved and appreciated greatly. We must have the weird function setup, so unfortunately I can't just make a nice simple while loop.
def mega_calculator(fn, repeat=1000):
def helper(*args):
total = 0
for _ in range(repeat):
total += fn(*args)
return total / repeat
return helper
def worst_hurricane(odds): """odds is a predefined function that tells us a random amount of property damage"""
index_variable = 1
big_boom = 0
place = 0
while index_variable <= 10:
y = mega_calculator(odds,50) """checking odds of damage for skyscrapers only, and finding the average after 50 times is what the function cal to mega_calculator does"""
print("building", a, "will have", y, "dollars of damage")
if y > big_boom:
big_boom = y
place = index_variable
elif y == big_boom:
place = max(place, index_variable)
index_variable +=
return place
`
mega_calculator is returning a function named helper, that you can call. Try code like this:
calculator = mega_calculator(odds)
y = calculator(50)
You also probably want to unindent index_variable += 4 positions to the left, and change it to index_variable += 1.
Here is what you are trying to do:
I am using some dummy function called, just to make you understand:
>>> def mega_calculator(some_function):
... def helper(*args):
... return some_function(*args)
... return helper
...
>>> def odds(*args):
... print args
...
>>> x = mega_calculator(odds)
>>> x
<function helper at 0x10c8f18c0>
>>>
>>> x = mega_calculator(odds)(['Here', 'are some' , 'argument'])
(['Here', 'are some', 'argument'],)
>>>

Categories