Python using string in dict() key? - python

Let say I have this base dictionary:
main = {'a': 10}
Then I have two other dictionaries which use main as starting point like this:
some_new_dict1 = dict(main, b=20, some_other_key=100)
some_new_dict2 = dict(main, c=20, some_other_key2=200)
But the thing is for b and for c keys, those are not fixed. It depends on other values.
In my case to evaluate what kind of key to assign to which dictionary is done like this:
some_map = {True: 'b', False: 'c'}
# Evaluate if its true or false
answer = something > 0 # something is variable that changes.
#It can be greater than zero or lower than zero (but not zero).
dict1_key, dict2_key = some_map[answer], some_map[not answer]
Now I have these keys in string form, but I don't see how can I assign it through dict. If its even possible.
So now I'm doing this:
some_new_dict1 = dict(main, some_other_key=100)
some_new_dict1[dict1_key] = 20
# Same for another dict
So basically I need to create dictionary and then update it with that one key/value pair even though I know that key/value pair before creating that dictionary. Is there some better way?

main = {'a': 5}
dict_key1 = 'test_key'
dict_val1 = 5
some_new_dict = dict(main, other_key=5, **{dict_key1: dict_val1})
Although, I find the two step way you use now more readable, so I think it's better.

If you want to opt for readability you can use copy, and perhaps also pack your mapping into the line:
some_new_dict1 = main.copy()
some_new_dict1[some_other_key] = 100
some_new_dict1['b' if answer else 'c'] = 20
By the way your comment #It can be greater than zero or lower than zero (but not zero). is false for your dict2_key. [not answer] includes zero, if it makes a difference for the logic of your programme.

Related

Is it okay to nest a dict.get() inside another or is this bad design?

So, I am working on a code base where a dictionary contains some key information. At some point in the development process the name of one of the keys was changed, but the older key still exists in a lot of places. Lets call the keys new and old for reference.
In order to make it compatible with the older version, I am doing something like:
dict_name.get(new_key,dict_name.get(old_key,None))
Is this bad design or is it okay? Why/Why not?
Example for clarification: (Based on input by #Alexander)
There are two dictionaries d1 and d2.
d1={k1:v1,old_key:some_value}
d2={k1:v1,new_key:some_value}
The function which I am designing right now could get either d1 or d2 like dictionary as an argument. My function should be able to pick up some_value, regardless of whether old_key or new_key is present.
That is a reasonable approach. The only downside is that it will perform the get for both keys, which will not affect performance in most situations.
My only notes are nitpicks:
dict is a reserved word, so don't use it as a variable
None is the default, so it can be dropped for old_key, e.g.:
info.get('a', info.get('b'))
In response to "Is there a way to prevent the double call?": Yup, several reasonable ways exist =).
The one-liner would probably look like:
info['a'] if 'a' in info else info.get('b')
which starts to get difficult to read if your keys are longer.
A more verbose way would be to expand it out into full statements:
val = None
if 'a' in info:
val = info['a']
elif 'b' in info:
val = info['b']
And finally a generic option (default after *keys) will only work with python 3):
def multiget(info, *keys, default=None):
''' Try multiple keys in order, or default if not present '''
for k in keys:
if k in info:
return info[k]
return default
which would let you resolve multiple invocations cleanly, e.g.:
option_1 = multiget(info, 'a', 'b')
option_2 = multiget(info, 'x', 'y', 'z', default=10)
If this is somehow a pandemic of multiple api versions or something (?) you could even go so far as wrapping dict, though it is likely to be overkill:
>>> class MultiGetDict(dict):
... def multiget(self, *keys, default=None):
... for k in keys:
... if k in self:
... return self[k]
... return default
...
>>> d = MultiGetDict({1: 2})
>>> d.multiget(1)
2
>>> d.multiget(0, 1)
2
>>> d.multiget(0, 2)
>>> d.multiget(0, 2, default=3)
3
dict.get is there for exactly this reason, so you can fall back on default values if the keys are not in there.
Having a double fallback is very much OK. For example:
d = {}
result = d.get('new_key',d.get('old_key', None))
This would mean that result is None in the worse case, but there is no error (which is the goal of get in the first place.
In other words, it will get the value of new_key as a first priority, old_key as the second priority, and None as a third.
Also worth noting that get(key, None) is the same as get(key) so you might want to shorten that line:
result = d.get('new_key', d.get('old_key'))
If you want to avoid calling get multiple times (for example, if you have to do more than 2 of those, it will be unreadable) you can do something like this:
priority = ('new_key', 'old_key', 'older_key', 'oldest_key')
for key in priority:
result = d.get(key)
if result is not None:
break
And result becomes whatever is encountered first in that loop, or None otherwise
Based on the sample dictionary provided, I would argue that this is bad design...
Lets say your original dictionary is:
d1 = {'k1': 1, 'k2': 2}
If I understand you correctly, you then 'update' one of the keys, e.g.:
d1 = {'k3': 1, 'k2': 2}
If you try to access via:
d1.get('k3', d1.get('k1')) # 'k3' is new key, 'k1' is old key.
then the first lookup will always be present and the second lookup will never be used.
If you meant that the new dictionary would looks like:
d2 = {'k1': 1, 'k2': 2, 'k3': 1}
then you are storing the 'same' data in two different locations in your dictionary, which will surely lead to trouble (similar to normalized data in a database). For example, if the value of 'k3' was updated to 3, then the value of k1 would need to be updated as well.
Given the dictionaries provided in your example:
d1={k1: v1, old_key: some_value}
d2={k1: v1, new_key: some_value}
I assume that some_value are intended to be equal in both, i.e. d1[old_key] == d2[new_key]. If so, then you could use d2.get(new_key, d1.get(old_key). However, it just seems like a mess.
If some_value needs to be updated, for example, it must be updated in both dictionaries.
You are wasting memory by storing the some_value twice.
Your new_key in d2 may accidentally clobber an existing key in d1.
I would recommend not changing the key names in the first place.

How to remove a key/value pair in python dictionary?

Say I have a dictionary like this :
d = {'ben' : 10, 'kim' : 20, 'bob' : 9}
Is there a way to remove a pair like ('bob',9) from the dictionary?
I already know about d.pop('bob') but that will remove the pair even if the value was something other than 9.
Right now the only way I can think of is something like this :
if (d.get('bob', None) == 9):
d.pop('bob')
but is there an easier way? possibly not using if at all
pop also returns the value, so performance-wise (as neglectable as it may be) and readability-wise it might be better to use del.
Other than that I don't think there's something easier/better you can do.
from timeit import Timer
def _del():
d = {'a': 1}
del d['a']
def _pop():
d = {'a': 1}
d.pop('a')
print(min(Timer(_del).repeat(5000, 5000)))
# 0.0005624240000000613
print(min(Timer(_pop).repeat(5000, 5000)))
# 0.0007729860000003086
You want to perform two operations here
1) You want to test the condition d['bob']==9.
2) You want to remove the key along with value if the 1st answer is true.
So we can not omit the testing part, which requires use of if, altogether. But we can certainly do it in one line.
d.pop('bob') if d.get('bob')==9 else None

Assigning values to objects attributes that don't exist

I'm doing a data mining homework with python(2.7). I created a weight dict for all words(that exist in the category), and for the words that don't exist in this dict, i want to assign a default value.
First I tried with setdefault for everykey before using it, it works perfectly, but somehow I think it doesn't look so pythonic. Therefore I tried using defaultdict, which works just fine most of the time. However, sometimes it returns an incorrect value. First I thought it could be caused by defaultdict or lambda function, but apparently there are no errors.
for node in globalTreeRoot.traverse():
...irrelevant...
weight_dict = {.......}
default_value = 1.0 / (totalwords + dictlen)
node.default_value = 1.0/ (totalwords + dictlen)
......
node.weight_dict_ori = weight_dict
node.weight_dict = defaultdict(lambda :default_value,weight_dict)
So, when I tried to print a value that doesn't exist during the loop, it gives me a correct value. However, after the code finishes running, when I try:
print node.weight_dict["doesnotexist"],
it gives me an incorrect value, and when incorrect usually a value related to some other node. I tried search python naming system or assign values to object attributes dynamically, but didn't figure it out.
By the way, is defaultdict faster than using setdefault(k,v) each time?
This is not a use case of defaultdict.
Instead, simply use get to get values from the dictionary.
val = dict.get("doesnotexist", 1234321)
is perfectly acceptable python "get" has a second parameter, the default value if the key was not found.
If you only need this for "get", defaultdict is a bit overkill. It is meant to be used like this:
example = defaultdict(list)
example[key].append(1)
without having to initialize the key-list combination explicitly each time. For numerical values, the improvements are marginal:
ex1, ex2 = dict, defaultdict(lambda: 0)
ex1[key] = ex1.get(key, 0) + 1
ex2[key] += 1
Your original problem probably is because you reused the variable storing the weight. Make sure it is local to the loop!
var = 1
ex3 = defaultdict(lambda: var)
var = 2
print ex3[123]
is supposed to return the current value of var=2. It's not substituted into the dictionary at initialization, but behaves as if you had define a function at this position, accessing the "outer" variable var.
A hack is this:
def constfunc(x):
return lambda: x
ex3 = defaultdict(constfunc(var))
Now constfunc is evaluated at initialization, x is a local variable of the invocation, and the lambda now will return an x which does not change anymore. I guess you can inline this (untested):
ex3 = defaultdict((lambda x: lambda: x)(var))
Behold, the magics of Python, capturing "closures" and the anomalies of imperative languages pretending to do functional programming.
setdefault is definitely what you should use to set a default value.
for node in globalTreeRoot.traverse():
node.default_value = 1.0 / (totalwords + dictlen)
node.weight_dict = {}
# if you did want to use a defaultdict here for some reason, it would be
# node.weight_dict = defaultdict(lambda: node.default_value)
for word in wordlist:
value = node.weight_dict.setdefault(word, node.default_value)
Apparently, there is something wrong with defaultdict.
d1 = {"a":10,"b":9,"c":8}
seven = 7
d2 = defaultdict(lambda :seven,d1)
seven = 8
d3 = defaultdict(lambda :seven,d1)
And the result:
>>> d2[4234]
8
I still don't understand why it works this way. As for my work, I'll stick with setdefault.
UPDATE:
Thanks for answering. I misunderstood how variable scoping works in Python.

Python references to references in python

I have a function that takes given initial conditions for a set of variables and puts the result into another global variable. For example, let's say two of these variables is x and y. Note that x and y must be global variables (because it is too messy/inconvenient to be passing large amounts of references between many functions).
x = 1
y = 2
def myFunction():
global x,y,solution
print(x)
< some code that evaluates using a while loop >
solution = <the result from many iterations of the while loop>
I want to see how the result changes given a change in the initial condition of x and y (and other variables). For flexibility and scalability, I want to do something like this:
varSet = {'genericName0':x, 'genericName1':y} # Dict contains all variables that I wish to alter initial conditions for
R = list(range(10))
for r in R:
varSet['genericName0'] = r #This doesn't work the way I want...
myFunction()
Such that the 'print' line in 'myFunction' outputs the values 0,1,2,...,9 on successive calls.
So basically I'm asking how do you map a key to a value, where the value isn't a standard data type (like an int) but is instead a reference to another value? And having done that, how do you reference that value?
If it's not possible to do it the way I intend: What is the best way to change the value of any given variable by changing the name (of the variable that you wish to set) only?
I'm using Python 3.4, so would prefer a solution that works for Python 3.
EDIT: Fixed up minor syntax problems.
EDIT2: I think maybe a clearer way to ask my question is this:
Consider that you have two dictionaries, one which contains round objects and the other contains fruit. Members of one dictionary can also belong to the other (apples are fruit and round). Now consider that you have the key 'apple' in both dictionaries, and the value refers to the number of apples. When updating the number of apples in one set, you want this number to also transfer to the round objects dictionary, under the key 'apple' without manually updating the dictionary yourself. What's the most pythonic way to handle this?
Instead of making x and y global variables with a separate dictionary to refer to them, make the dictionary directly contain "x" and "y" as keys.
varSet = {'x': 1, 'y': 2}
Then, in your code, whenever you want to refer to these parameters, use varSet['x'] and varSet['y']. When you want to update them use varSet['x'] = newValue and so on. This way the dictionary will always be "up to date" and you don't need to store references to anything.
we are going to take an example of fruits as given in your 2nd edit:
def set_round_val(fruit_dict,round_dict):
fruit_set = set(fruit_dict)
round_set = set(round_dict)
common_set = fruit_set.intersection(round_set) # get common key
for key in common_set:
round_dict[key] = fruit_dict[key] # set modified value in round_dict
return round_dict
fruit_dict = {'apple':34,'orange':30,'mango':20}
round_dict = {'bamboo':10,'apple':34,'orange':20} # values can even be same as fruit_dict
for r in range(1,10):
fruit_set['apple'] = r
round_dict = set_round_val(fruit_dict,round_dict)
print round_dict
Hope this helps.
From what I've gathered from the responses from #BrenBarn and #ebarr, this is the best way to go about the problem (and directly answer EDIT2).
Create a class which encapsulates the common variable:
class Count:
__init__(self,value):
self.value = value
Create the instance of that class:
import Count
no_of_apples = Count.Count(1)
no_of_tennis_balls = Count.Count(5)
no_of_bananas = Count.Count(7)
Create dictionaries with the common variable in both of them:
round = {'tennis_ball':no_of_tennis_balls,'apple':no_of_apples}
fruit = {'banana':no_of_bananas,'apple':no_of_apples}
print(round['apple'].value) #prints 1
fruit['apple'].value = 2
print(round['apple'].value) #prints 2

Python dictionary error

In the below code d_arr is an array of dictionaries
def process_data(d_arr):
flag2 = 0
for dictionaries in d_arr:
for k in dictionaries:
if ( k == "*TYPE" ):
""" Here we determine the type """
if (dictionaries[k].lower() == "name"):
dictionaries.update({"type" : 0})
func = name(dictionaries)
continue
elif (dictionaries[k].lower() == "ma"):
dictionaries.update({"type" : 1})
func = DCC(dictionaries)
logging.debug(type(func))
continue
When the above is done i get an error saying
for k in dictionaries:
RuntimeError: dictionary changed size during iteration
2010-08-02 05:26:44,167 DEBUG Returning
Is this forbidden to do something like this
It is, indeed, forbidden. Moreover, you don't really need a loop over all keys here, given that the weirdly named dictionaries appears to be a single dict; rather than the for k in dictionaries: (or the workable for k in dictionaries.keys() that #Triptych's answer suggests), you could use...:
tp = dictionaries.get('*TYPE')
if tp is not None:
""" Here we determine the type """
if tp.lower() == 'name':
dictionaries.update({"type" : 0})
func = name(dictionaries)
elif tp.lower() == "ma":
dictionaries.update({"type" : 1})
func = DCC(dictionaries)
logging.debug(type(func))
This is going to be much faster if dictionaries has any considerable length, for you're reaching directly for the one entry you care about, rather than looping over all entries to check each of them for the purpose of seeing if it is the one you care about.
Even if you've chosen to omit part of your code, so that after this start the loop on dictionaries is still needed, I think my suggestion is still preferable because it lets you get any alteration to dictionaries done and over with (assuming of course that you don't keep altering it in the hypothetical part of your code I think you may have chosen to omit;-).
That error is pretty informative; you can't change the size of a dictionary you are currently iterating over.
The solution is to get the keys all at once and iterate over them:
# Do this
for k in dictionaries.keys():
# Not this
for k in dictionaries:

Categories