This question already has answers here:
How to access (get or set) object attribute given string corresponding to name of that attribute
(3 answers)
Closed 6 years ago.
I'm programming the board game Monopoly in Python. Monopoly has three types of land that the player can buy: properties (like Boardwalk), railroads, and utilities. Properties have a variable purchase price and rents for 6 conditions (0-4 houses, or a hotel). Railroads and utilities have a fixed price and rents based on how many other railroads or utilities you own.
I have a Game() class that contains three dictionary attributes, all whose key is the land parcel's position on the board from 0-39:
.properties, whose values are a list containing the space's name, buy price, color group and rents (tuple);
.railroads, which consists only of the space name;
.utilities, also containing only the space name.
I did this because at certain points I want to iterate over the appropriate dictionary to see if the player owns other parcels of land in that dictionary; and also because the number of values differs.
Game() also has a tuple called space_types, where each value is a number representing a type of space (property, railroad, utility, luxury tax, GO, etc.). To find out what kind of space_type my player is sitting on:
space_type = space_types[boardposition]
I also have a Player() class with a method buy_property(), which contains a print statement that should say:
"You bought PropertyName for $400."
where PropertyName is the name of the space. But right now I have to use an if/elif/else block like so, which seems ugly:
space_type = Game(space_types[board_position])
if space_type is "property":
# pull PropertyName from Game.properties
elif space_type is "railroad":
# pull PropertyName from Game.railroads
elif space_type is "utility":
# pull PropertyName from Game.utilities
else:
# error, something weird has happened
What I'd like to do is something like this:
dictname = "dictionary to pull from" # based on space_type
PropertyName = Game.dictname # except .dictname would be "dictionary to pull from"
Is it possible in Python to pass the value of a variable as the name of an attribute to be referenced? I will also appreciate someone telling me I'm approaching this fundamentally wrong and suggesting a better way to go about it.
Use the getattr built-in:
PropertyName = getattr(Game, dictname)
http://docs.python.org/2/library/functions.html#getattr
You can use the getattr function:
property_name = getattr(Game, dictname)
How about a dictionary of dictionaries?
D= {"property": Game.properties, "railroad": Game.railroads, "utility": Game.utilities}
space_type = Game(space_types[board_position])
dictname = D[space_type]
Related
Basically, I want to store objects with two attributes (name, score) in a list. From there I want to find the object in the list with the highest score, and be able to access the name of that object. So if 50 is determined to be the highest score in the list, I wan't to be able to see that this score belongs to "wages".
Here is my starting code where I create the objects with set attributes (for testing).
Storing the objects in the list works just fine, and I can access individual attributes in a for-loop (for i in a_list: print(i.score)) for example. But when it comes to accessing the maximum score and get the name associated with that object I am at a loss.
class Scoreboard:
def __init__(self, name, score):
self.name = name
self.score = int(score)
t1 = Scoreboard("wages", 50)
t2 = Scoreboard("todo", 15)
t3 = Scoreboard("invoice", 36)
a_list = []
a_list.append(t1)
a_list.append(t2)
a_list.append(t3)
I tried using max() but that returns an error unless I only store the score value (ex. t1.score) in the list. If I do that I can't find a way to access the name attribute of that object.
Try this:
m = max(a_list, key=lambda s: s.score)
That should use the score attribute of each object in the list as the key when finding the max.
Python has no way of knowing what Scoreboard is, and that it is supposed to use the .score attribute for calculating maxs or mins. As in the comments, a dictionary seems more appropriate. If you want to go on with classes you need to ask python to give you the name of the instance with score equal to max(scores). Something like:
[(t.name, t.score) for t in a_list if t.score == max(t.score for t in a_list)]
>>[('wages', 50)]
I am a novice Python user so I hope I haven't missed something basic but I feel I've done my due diligence in trying to research this problem on my own so here goes.
In brief, I am writing a program that will analyze sports statistics and ultimately generate a rating for the strength of each team based on the chosen statistics.
I can successfully read in simple csv files and I'm reasonably happy with the custom object class I have created to store the statistics as attributes for each team but I am running in to an issue when I go to calculate the rating. Essentially, I need to sort all the teams by each statistic I am interested in, rank the teams by this statistic and assign a point value for the rank of each one. This will produce a cumulative rating score based on the rank for each statistic. However, I'm having some issues in getting the statistic value as a float which I think I need to do in order to sort properly.
Here's the code I've tried:
I've created a team object as seen below. This version is stripped down of most of the attributes for ease of reading but the additional attributes are all very similar.
class team(object):
def__init__(self,teamName="",passOffYc=0, passOffAya=0):
self.teamName = teamName
self.passOffYc = passOffYc
self.passOffAya = passOffAya
self.score = 0
These objects are constructed from a csv file that has a header with the statistical categories and each row represents a team and its stats. I am reading in the file using csv.DictReader like this:
league= []
with open(passoffense) as csvfile:
statreader = csv.DictReader(csvfile, delimiter=',')
for row in statreader:
newTeam = team(row["Tm"],row["Y/C"],row["AY/A"])
print(newTeam, "added")
league.append(newTeam)
At this point I think I have a list called league that contains a team object for each row in the csv file and the teamname, passOffYc, and passOffAya attributes have taken the values for the row elements Tm, Y/C, and AY/A. These are the team name, Yards per Catch, and Average Yards per Attempt so the second two are all decimal numbers.
To try to create the score for each team object, I'd like to sort the league list first by passOffYc, determine the rank of each team, and then repeat for passOffAya and so on for all the attributes in the full version of the program.
I've attempted two different implementations of this trying to understand attrgetter or lambda functions.
My attrgetter thoughts are something like this:
sortedTeams = sorted(league, key = attrgetter("passOffYc"))
My understanding is that this would sort the list called league according to the attribute passOffYc but the issue I'm encountering is that the sort is not producing the expected output.
If passOffYc was 19, 23, 14, 7, and 9, I am expecting the sort to result in 7, 9, 14, 19, 23. However it will end up sorting as 14, 19, 23, 7, 9. My research has led me to believe this is because the values are strings and not integers (or more accurately floats as some values have decimals) Not quite sure how to fix this I tried:
sortedTeams = sorted(league, key = float(attrgetter("passOffYc"))
But this gives me the error:
TypeError: float() argument must be a string or a number, not 'operator.attrgetter'
So apparently it isn't a string and instead is an operator.attrgetter object. I haven't been able to figure out how to get the key for the sorted function to the float type so I also tried using lambda functions I read about while searching:
sortedTeams = sorted(league, key = lambda team: float(team.passOffYc))
This seems very close to what I'd like to happen as it does sort properly but then I run in to a scaling problem. Since I have more than 20 attributes to sort by in the full version of my program I'd like to avoid needing to type the above statement 20 times to accommodate each attribute.
I thought of trying to define a function something to the effect of:
def score(stat):
sortedTeams = sorted(league, key = lambda team: float(team.stat))
I thought this would allow me to pass in to the lambda function which stat I want to sort by but I then get the error:
AttributeError: 'team' object has no attribute 'stat'
I think this is because it may not be possible to pass an argument to a lambda function or that I'm not understanding something because I also tried:
sortedTeams = sorted(league, key = lambda team, stat=stat: float(team.stat))
Which resulted in the same error. Whew! If you're still reading this thank you for wading through my essay. Any ideas how I can solve this?
Once I get the sorting to work properly and can scale it I intend to enumerate the sorted lists to obtain the ranks and I'm beginning to think about strategies to address ties. Thank you again for any help!
You just need to create the original team entries with float contents:
newTeam = team(row["Tm"],float(row["Y/C"]),float(row["AY/A"]))
If instead you want to pursue the lambda approach you can use:
sortedTeams = sorted(league, key = lambda team: float(attrgetter("passOffYc")(team)))
This could similarly be used in your function score function. What you were missing is that attrgetter returns a function. You can then call that function by passing it an argument (in this case team). Then that result can be passed to float. In that function you could use:
lambda team: float(attrgetter(stat)(team))
As I understand, you whant to pass a string name of desired field into function. If that is right, then instead of:
def score(stat):
sortedTeams = sorted(league, key = lambda team: float(team.stat))
Try this:
def score(stat):
sortedTeams = sorted(league, key = lambda team: float(getattr(team, stat)))
Some explanation. team.stat - accessing an attribute with name "stat" for object team. getattr(team, stat) - accessing an attribute with name given in the stat variable for object team.
I'm using the Field Calculator in ArcMap and
I need to create a unique ID for every storm drain in my county.
An ID Should look something like this: 16-I-003
The first number is the municipal number which is in the column/field titled "Munic"
The letter is using the letter in the column/field titled "Point"
The last number is simply just 1 to however many drains there are in a municipality.
So far I have:
rec=0
def autoIncrement()
pStart=1
pInterval=1
if(rec==0):
rec=pStart
else:
rec=rec+pInterval
return "16-I-" '{0:03}'.format(rec)
So you can see that I have manually been typing in the municipal number, the letter, and the hyphens. But I would like to use the fields: Munic and Point so I don't have to manually type them in each time it changes.
I'm a beginner when it comes to python and ArcMap, so please dumb things down a little.
I'm not familiar with the ArcMap, so can't directly help you, but you might just change your function to a generator as such:
def StormDrainIDGenerator():
rec = 0
while (rec < 99):
rec += 1
yield "16-I-" '{0:03}'.format(rec)
If you are ok with that, then parameterize the generator to accept the Munic and Point values and use them in your formatting string. You probably should also parameterize the ending value as well.
Use of a generator will allow you to drop it into any later expression that accepts an iterable, so you could create a list of such simply by saying list(StormDrainIDGenerator()).
Is your question on how to get Munic and Point values into the string ID? using .format()?
I think you can use following code to do that.
def autoIncrement(a,b):
global rec
pStart=1
pInterval=1
if(rec==0):
rec=pStart
else:
rec=rec+pInterval
r = "{1}-{2}-{0:03}".format(a,b,rec)
return r
and call
autoIncrement( !Munic! , !Point! )
The r = "{1}-{2}-{0:03}".format(a,b,rec) just replaces the {}s with values of variables a,b which are actually the values of Munic and Point passed to the function.
This question already has answers here:
Dictionary Help! Extracting values and making a table
(2 answers)
Closed 8 years ago.
Here's the question that I'm supposed to code for:
Write the contract, docstring and implementation for a function showCast that takes a movie title and prints out the characters with corresponding actors/actresses from the given movie in an alphabetical order of characters. The columns must be aligned (20 characters (including the character's name) before the name of the actor/actress.) If the movie is not found, it prints out an error message.
It gives an example of what's supposed to happen here
>>> showCast("Harry Potter and the Sorcerer's Stone")
Character Actor/Actress
----------------------------------------
Albus Dumbledore Richard Harris
Harry Potter Daniel Radcliffe
Hermione Granger Emma Watson
Ron Weasley Rupert Grint
>>> showCast('Hairy Potter')
No such movie found
Here are other functions that I've written for the same project that will probably be of assistance in answering the question. A summary of what I've had to do so far is that I'm creating a dictionary, called myIMDb, with a key of the title of the movie, and the value another dictionary. In that dictionary that key is a character of a movie, and the value is the actor. And I've done stuff with it. myIMDb is a global variable for the record.
Other functions, what they do is the docString
def addMovie (title, charList, actList):
"""The function addMovie takes a title of the movie, a list of characters,
and a list of actors. (The order of characters and actors match one
another.) The function addMovie adds a pair to myIMDb. The key is the title
of the movie while the value is a dictionary that matches characters to
actors"""
dict2 = {}
for i in range (0, len(charList)):
dict2 [charList[i]] = actList[i]
myIMDb[title] = dict2
return myIMDb
I've added three movies,
addMovie("Shutter Island", ["Teddy Daniels", "Chuck Aule"],["Leonardo DiCaprio, ","Mark Ruffalo"])
addMovie("Zombieland", ["Columbus", "Wichita"],["Jesse Eisenberg, ","Emma Stone"])
addMovie("O Brother, Where Art Thou", ["Everett McGill", "Pete Hogwallop"],["George Clooney, ","John Turturro"])
def listMovies():
"""returns a list of titles of all the movies in the global variable myIMDb"""
return (list(myIMDb.keys()))
def findActor(title, name):
""" takes a movie title and a character's name and returns the
actor/actress that played the given character in the given movie. If the
given movie or the given character is notfound, it prints out an error
message"""
if title in myIMDb:
if name in myIMDb[title]:
return myIMDb[title][name]
else:
return "Error: Character not in Movie"
else:
return "Error: No movie found"
Now where I'm having trouble
I'm supposed to write the showCast function, but I'm having a lot of trouble. I've been tinkering with it for a while but when I call myIMDb.values() everything returns. And I can't seem to loop through it to sort them to create the table.
Here's what I've come up with so far, but it doesn't do what I was hoping. I'm just hoping that one of you can steer me in the right direction. (The commented out region is what I was doing before, just so you can see my train of thought. [the print(alist) and print(alist[0]) was just to confirm that it's one big entry in a list, not separated at all])
def showCast(title):
if title in myIMDb:
actList=[]
chList=[]
aList = list(myIMDb.values())
print (aList)
print (aList[0])
""""for i in range (len(aList)):
if i%2==0:
chList.append(aList[i])
else:
actList.append(aList[i])
print(chList)
print(actList)""""
else:
return "Movie not Found"
It's an old question, but I'll take a stab. I think your confusion comes from the nested nature of the myIMDb object. To get back information about a specific movies, you should use the title as a key to myIMDb, e.g. myIMDb[title]. What you get back is another dictionary, that you can then use to get the character/actor key value pairs.
Here's a working version of the showCast function:
def showCast(title):
if title in myIMDb:
print("{0:20} {1:20}".format("Character", r"Actor/Actress"))
print("-"*40)
for character, actor in myIMDb[title].items():
print("{0:20} {1:20}".format(character, actor))
else:
return "Movie not Found"
The first print statement generates the heading, and uses the Python's format string method to get the aligned spacing that you want. The next print statement is the divider, and then the meat of the function is simply iterating over the pairs with a for loop.
I hope that helps.
Can someone explain to me how to return a new instance of a class by iterating over a list of names and a list of dicts?
I know that I can unpack my dictionary using **, and I know that I can iterate through my list of dictionaries.
So I attempted to create a for loop function that creates new instances of my Card class for each card in the cardList.
Here is the code I thought would work:
def createCardInstances(x):
for i in range(len(x)):
namesOfCards[i] = Card(**cardList[i])
return namesOfCards[i]
namesOfCards is a list of card names that I would like to turn into instances of the Card class. cardList is a list of the dictionaries that represent cards and their values.
For instance, I can do this:
Knight = Card(**cardList[0])
Which works fine. But, if I'm developing a card game and have upwards of a 100 individual cards or more, I would like to avoid having to write out each card name individually and copy\pasting the =Card(**cardList[]) code each time.
How do I automate the creation of new instances? Is that even feasible?
EDIT:
I'm trying to use the names in namesOfCards to make new instances of Card. It was suggested to me that my next step in learning python while creating a card game was to make it more object oriented.
So my goal right now is to use the names of the cards - saved in the list namesOfCards - to make new instances of Card. i.e.:
[Knight, Mage, Warrior, ...]
And I want to have the ability to take those and do this:
Knight = Card(**cardList[0])
Mage = Card(**cardList[1])
Warrior = Card(**cardList[2]
and so on and so forth.
Is that possible? Or do I need to use the list comprehension suggestion to store all of the class instances into a list, and then have to access the instances by using new_cards[0].name or new_cards[1].attack?
you are returning an individual card when you pass it a list? also if cardNames is a list of names you shouldn't be overwriting it with Card objects. You should be able to do something like this?
new_cards = [Card(**card) for card in card_list] # make new card objects
cards = {card.name: card for card in new_cards} # dict to ref cards by name
print cards['Knight'].name
or simply
cards = {card['name']: Card(**card) for card in card_list}
if you are wanting to restrict that cards to only those in namesOfCards. you could
cards = {card['name']: Card(**card) for card in card_list if card['name'] in names_of_cards}
(note: this all assumes that you dont have two cards with the same name)
It is also possible to put those cards into your local namespace like the question asks, but I would highly discourage it. But if you really really want to ...
locals().update(cards)
print Knight.name
make sure that your list contains string like
CARDS = ['Knght', 'Mage'] #and so on
I'm not sure if this is what you need but see the code below:
CARD = ['knight','mage']
class cards:
def __init__(self, name):
self.n = name
self.attack = "attack " + self.n
print knight.attack
using that you can loop through a list or dictionary to create an instance of your class