python reorder dictionary based on a combination of values - python

I am trying to write a targeting priority script for an AI. My goal is to rank targets based on their score of damage_per_shot / rate_of_fire and reorder the list based on highest targeting priority. I finally hit an error I didn't know how to work around however. 'NoneType' object has no attribute 'get'
I am very new to Python and built this mostly by Googling the terms I would have used in Ruby. I would also appreciate suggestions about how to do this in the correct Python "style" if I made any major errors.
enemyList=[{"id":1,"damage_per_shot":10,"rate_of_fire":2},{"id":3,"damage_per_shot":0,"rate_of_fire":0},{"id":2,"damage_per_shot":14,"rate_of_fire":2}]
#enemyList=unit_client.ask_nearest_enemy()
print(enemyList)
aDict = {}
for item in enemyList:
if(item["rate_of_fire"]!=0):
currScore=float(item["damage_per_shot"]/item["rate_of_fire"])
aDict[item['id']] = currScore
def focus_fire2(data=None, *args, **kawargs):
print("===ff2===")
target_id=sorted(aDict, key=data.get)
print(target_id)
print("attacking: "+str(id))
#unit_client.do_attack(key)
##remove item from list
if(len(aDict)>0):
del aDict[target_id] #remove the object from the dict after done
print(aDict)
focus_fire2()
else:
return 0
#unit_client.when_item_destroyed(target, aDict.pop(key,None))
#unit_client.when_item_destroyed(target, focus_fire2)
focus_fire2()
The traceback looks like
[{'damage_per_shot': 10, 'id': 1, 'rate_of_fire': 2}, {'damage_per_shot': 0, 'id
': 3, 'rate_of_fire': 0}, {'damage_per_shot': 14, 'id': 2, 'rate_of_fire': 2}]
===ff2===
Traceback (most recent call last):
File "ffire.py", line 26, in <module>
focus_fire2()
File "ffire.py", line 13, in focus_fire2
target_id=sorted(aDict, key=data.get)
AttributeError: 'NoneType' object has no attribute 'get'

Just replace
sorted(aDict, key=data.get)
by
sorted(aDict, key=aDict.get)

In your function focus_fire2, you set data to None by default in the arguments of the function definition. Unless you set it to something other than None when you call it, like focus_fire2(data=something), then it's equal to None when you do, data.get() later on. And there lies your error I think. You are treating a NoneType like a dict. If you're calling get on it, you probably should be setting it equal to some dict or other.

You get an error, because run function without arguments, and by default data have a None type. And for me it's not good idea, set default value as None for non optional arguments. You need run function with argument focus_fire2(aDict), or change target_id=sorted(aDict, key=data.get) to target_id=sorted(aDict, key=aDict.get, reverse=True) you get in target_id, list of sorted key from aDict, where first value is higher. But target_id is a list and this code is wrong del aDict[target_id]. What must be in target_id?

Related

How to get index position of values in python

I have a scenario , where I am trying to get index position of value
My code :
a_set = {22,56,26}
print(a_set[56])
Getting below error
Traceback (most recent call last):
File "<string>", line 5, in <module>
TypeError: 'set' object is not subscriptable
Expected output :
1 -> This the position of 56 from set
The error is explaining a lot here: sets in Python are not subscriptable.
They dont have order relation.
According to your code example, you are trying to ask weather a value exists in the set, right?
In Python you can do it with in operator:
>> print(36 in a_set)
True
or
if (36 in a_set):
my_function()
Sets are by definition completely unordered and unindexed, you cannot get the information with an index directly as that is not what they were made for. As a workaround, you can simply convert the set to a list that is both indexed and ordered.
a_set = {22,56,26}
print(list(a_set)[3]) # converts the set into and displays it's third entry.
To solve your problem, you can use .index() on the new list such as this:
a_set = {1,2,3}
print(list(a_set).index(1))

Error in access to python dictionary

I'm pretty sure that my error is a very small and stupid thing but I'm not capable to see it!! :(
I have a defined dictionary: self.names_to_nodes, and I want to access to one of its members with the key that I have.
Here is a piece of my code:
print "Result " + str(len( self.names_to_nodes))
if i in self.names_to_nodes:
print "ESTA"
member = self.names_to_nodes.get(i)
ancestrosA.append(member.get_parent())
And I get this exit and this error:
Result 17
ESTA
143 print "ESTA"
144 member = self.names_to_nodes.get(i)
145 ancestrosA.append(member.get_parent())
146 i = member.get_parent()
147 ancestrosA.append(self.founder)
AttributeError: 'NoneType' object has no attribute 'get_parent'
How is this possible???
Thanks for your help! How get(i) is not finding the element if the key is in the dictionary?
BR,
Almu
You need to move the lookup inside the if statement if you want to make sure it exists before checking:
if i in self.names_to_nodes:
print "ESTA"
# only does a lookup if the key exists
member = self.names_to_nodes[i]
ancestrosA.append(member.get_parent())
You check if it exists but still check i outside the if so whether i exists or not you always do a lookup.
dict.get returns a value whether the key is in the dictionary or not. Example:
>>> x = {1:2}
>>> print(x.get(100))
None
Try using regular item access instead. Then your code will raise an exception if you try to get a nonexistent value. Example:
>>> x = {1:2}
>>> print(x[100])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 100
(Readers may ask, "but how could i not be a key in the dictionary? 'ESTA' was printed, and that only occurs when the membership test successfully passes". I'm assuming that this code is inside a for loop that changes the value of i, and the printed 'ESTA' is from the previous iteration, which ran without problems.)

How do I fix the RuntimeError in this program?

This program is a function that takes dictionary and it must return a new dictionary with mirror image of the original dictionary (meaning key:value pairs are switched).
However, according to pythontutor.com, for the line of code that says the for loop, it throws a RuntimeError.
I am using the latest version of Python (3.4.1)
#Program purpose: Write a function called rvBirthday that takes
# dictionary birthday as input. It returns a
# mirror image of the dictionary, where birthday
# is the key and name is the value.
def rvBirthday(birthday):
reverseBD = {}
for key in birthday.keys():
date = birthday.get(key)
birthday.pop(key)
reverseBD[date] = key
return reverseBD
birthday = {'Mitsuyuki Washida':'3-29-93', 'Joe Bob':'7-12-96',
'Sam Wilson':'4-1-02'}
print(rvBirthday(birthday))
The error I get is:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in rvBirthday
RuntimeError: dictionary changed size during iteration
You are altering the input dictionary while looping over it by using dict.pop(). This changes the dictionary size, and that breaks iteration.
Your instructions say nothing about removing keys from the input dictionary. Remove the dict.pop() calls altogether.
You don't need to use .keys() or .get() here either. Looping over a dictionary yields keys, so you don't have to use a separate method to extract the keys. You then know that those keys are in the dictionary, so .get() to return default if it is missing is also redundant.
Better loop over the dictionary items; this gives you both the key and the value in one step:
def rvBirthday(birthday):
reverseBD = {}
for key, date in birthday.items():
reverseBD[date] = key
return reverseBD
This can be expressed with a dictionary comprehension too:
def rvBirthday(birthday):
return {date: key for key, date in birthday.items()}
If you still need to clear the input dictionary, simply add a birthday.clear() call after copying across the key-value pairs.

Appending raw input to a list

This is the simplest of exercises. I just don't understand why it wont work.
Here's my code:
hobbies = []
for i in range(3):
hobby = raw_input("Name a hobby")
hobbies = hobbies.append(hobby)
Basically I want to ask my user 3 times to name one of his hobbies, and store them in a list. But for some reason I'm getting this error,
Traceback (most recent call last):
File "C:/Python27/hobbies.py", line 4, in <module>
hobbies = hobbies.append(hobby)
AttributeError: 'NoneType' object has no attribute 'append'
which I don't really understand.
The problem is that append() will change the list in-place. And when you call this function no value is returned.
The first time you get a None value for the variable hobbies. The second time you try to call the append() method for a None value...
You should not use hobbies = hobbies.append(). Instead use hobbies.append() only.

Adding a new key to a nested dictionary in python

I need to add a key with a value that increases by one for every item in the nested dictionary. I have been trying to use the dict['key']='value' syntax but can't get it to work for a nested dictionary. I'm sure it's a very simple.
My Dictionary:
mydict={'a':{'result':[{'key1':'value1','key2':'value2'},
{'key1':'value3','key2':'value4'}]}}
This is the code that will add the key to the main part of the dictionary:
for x in range(len(mydict)):
number = 1+x
str(number)
mydict[d'index']=number
print mydict
#out: {d'index':d'1',d'a'{d'result':[...]}}
I want to add the new key and value to the small dictionaries inside the square parentheses:
{'a':{'result':[{'key1':'value1',...,'index':'number'}]}}
If I try adding more layers to the last line of the for loop I get a traceback error:
Traceback (most recent call last):
File "C:\Python27\program.py", line 34, in <module>
main()
File "C:\Python27\program.py", line 23, in main
mydict['a']['result']['index']=number
TypeError: list indices must be integers, not unicode
I've tried various different ways of listing the nested items but no joy. Can anyone help me out here?
The problem is that mydict is not simply a collection of nested dictionaries. It contains a list as well. Breaking up the definition helps clarify the internal structure:
dictlist = [{'key1':'value1','key2':'value2'},
{'key1':'value3','key2':'value4'}]
resultdict = {'result':dictlist}
mydict = {'a':resultdict}
So to access the innermost values, we have to do this. Working backwards:
mydict['a']
returns resultdict. Then this:
mydict['a']['result']
returns dictlist. Then this:
mydict['a']['result'][0]
returns the first item in dictlist. Finally, this:
mydict['a']['result'][0]['key1']
returns 'value1'
So now you just have to amend your for loop to iterate correctly over mydict. There are probably better ways, but here's a first approach:
for inner_dict in mydict['a']['result']: # remember that this returns `dictlist`
for key in inner_dict:
do_something(inner_dict, key)
I'm not fully sure what you're trying to do, but I think itertools.count would be able to help here.
>>> c = itertools.count()
>>> c.next()
0
>>> c.next()
1
>>> c.next()
2
>>> c.next()
3
... and so on.
Using this, you can keep incrementing the value that you want to use in your dicts
Hope this helps

Categories