I wonder how to accomplish something like this in python:
d = {'a': 3}
a_value_ref = d['a']
a_value_ref = 6
assert d['a'] == 6
I want to first calculate reference to a specific value in a multilevel dict and then modify it by using this mechanism. Is it possible? It's easy using C/C++. Thank you for your help.
You can't do that, because integers assignment changes its reference, and you cannot change its value without that (immutability of integers)
You could do that using a single integer in a list, that would work.
d = {'a': [3]}
a_value_ref = d['a']
a_value_ref[0] = 6
assert d['a'] == [6]
This is not a way how Python works, workarounds are not a good solutions. I have changed the way of accessing dict's keys and now I update values using the dict[key] = value syntax.
Related
ky = ['a','b','c']
val = 15
dict_1 = dict.fromkeys(ky,[])
dict_1['a'].append(val)
dict_2 = {'a':[],'b':[],'c':[]}
dict_2['a'].append(val)
print(dict_1)
print(dict_2)
they are all using the same ref when you use dict.fromkeys() A change to one is a change to all since they are the same object
You could use a dict comprehension instead of append:
keys = ['a','b','c']
value = [0, 0]
{key: list(value) for key in keys}
{'a': [0, 0], 'b': [0, 0], 'c': [0, 0]}
Please correct me if I'm wrong, but given your post, I'm presuming your question is why dict_1 has the same value (15) for all its keys a, b, and c as the end result, right?
If so, I agree that this is unintuitive behavior at a first glance, but what is happening is that by passing [] (an empty list) as the second optional argument for the fromkeys method, you're populating dict_1 dictionary with references to the same single empty list, hence, when you append val to the key a, all other keys are updated with the same value as well (you passed the empty list as reference. see difference between passing by reference vs passing by value ).
As you noticed yourself, there are other ways to initialize a dictionary of empty lists in Python (as you did "manually" with dict_2).
In case you're interested in another "less manual" way to initialize a dictionary of empty lists in Python, you could use a list comprehension to do so, as in the following example:
dict_3 = {k : [] for k in ky}
That would initialize dict_3 with empty lists (passed by value), thus, keys b and c would be unaffected when you append val to the key a.
I hope that helps to clarify your example. Kind regards.
With dict_1 = dict.fromkeys(ky,[]), all keys are mapped to the same (empty) array.
So whenever you change the contents of that array, each one of these keys will (still) point to the same array containing these new contents.
dict_1 = dict.fromkeys(ky,[]) creates a single list and then calls fromkeys. That one list is used for the value paired with each key. One list referenced through all of the keys.
If you want unique lists, use a dictionary comprehension
dict_3 = {k:[] for k in ky}
Here, k:[] is evaluated for each key and a new list is created for each value.
I need to create multiple dictionaries in one line, I tried like following.
a,b,c = dict(), dict(), dict()
Is there any pythonic way to achieve this? I tried with
a = b = c = dict()
But in this, if I change a it also reflects with other dicts
a['k'] = 'val'
a
{'k': 'val'}
b
{'k': 'val'}
c
{'k': 'val'}
I'm just posting some thoughts here:
Pep 8 is a style guide for python code: https://www.python.org/dev/peps/pep-0008/. However nothing about declaring variables there.
Although these work:
a,b,c = dict(), dict(), dict()
a, b, c = [dict() for _ in range(3)]
I think this is the most readable:
a = dict()
b = dict()
c = dict()
Reason:
You can always expect that variables are defined on separate rows. What about if you were to assign 20 items, would it be: a,b,c,d,e.... ??
Anyhow, another way of doing it would be to nest them inside one dictionary, and here too only one variable is declared:
dicts = {letter:dict() for letter in list("abc")} # {'a': {}, 'b': {}, 'c': {}}
Your first method is fine. use
a,b,c = dict(), dict(), dict()
The explanation for the second method :
Python variables are references to objects, but the actual data is
contained in the objects.
a = b = c = dict() is not creating three dict. In python, variables don't store the value. Variables point to the object and objects store the value, so here a,b,c variable pointing same object which contains one dict(). you can check
print(id(a),id(b),id(c))
4321042248 4321042248 4321042248
That's why when you change in one, it changes the other too because they are holding the same dict value.
I have a nested dict like this, but much larger:
d = {'a': {'b': 'c'}, 'd': {'e': {'f':2}}}
I've written a function which takes a dictionary and a path of keys as input and returns the value associated with that path.
>>> p = 'd/e'
>>> get_from_path(d, p)
>>> {'f':2}
Once I get the nested dictionary, I will need to modify it, however, d can not be modified. Do I need to use deepcopy, or is there a more efficient solution that doesn't require constantly making copies of the dictionary?
Depending on your use case, one approach to avoid making changes to an existing dictionary is to wrap it in a collections.ChainMap:
>>> import collections
>>> # here's a dictionary we want to avoid dirty'ing
>>> d = {i: i for in in range(10)}
>>> # wrap into a chain map and make changes there
>>> c = collections.ChainMap({}, d)
Now we can add new keys and values to c without corresponding changes happening in d
>>> c[0] = -100
>>> print(c[0], d[0])
-100 0
Whether this solution is appropriate depends on your use case ... in particular the ChainMap will:
not behave like a regular map when it comes to some things, like deleting keys:
>>> del c[0]
>>> print(c[0])
0
still allow you to modify values in place
>>> d = dict(a=[])
>>> collections.ChainMap({}, d)["a"].append(1)
will alter the list in d
However, if you are merely wishing to take your embedded dictionary and pop some new keys and values on it, then ChainMap may be appropriate.
How would I solve this issue?
d = {
'a':3,
'b':(d['a']*3)
}
print(d)
results in an error because I try to set the value of 'b' using the name of the dictionary, and apparently python does not like that. How would I get around this?
Also:
l = [[2], [(l[0][0]*2)]]
print(l)
has the same issue.
Given how assignment works - the RHS expression is eval'd first, and only then is the resulting object bound to the LHS -, that was to be expected : you cannot reference a name that has not been yet created in the current scope.
The solutions are either to use an intermediate variable for the value you want to reuse, as explained in lambo's answer, or to first build the dict (or list or whatever) with the first key or index/value pair then build the other, ie:
d = {"a", 3}
d["b"] = d["a"] * 3
Assign the values to variables first:
x = 3
d = {'a': x, 'b': x*3}
y = 2
l = [[y], [y*2]]
I'm going through a whole bunch of tuples with a many-to-many correlation, and I want to make a dictionary where each b of (a,b) has a list of all the a's that correspond to a b. It seems awkward to test for a list at key b in the dictionary, then look for an a, then append a if it's not already there, every single time through the tuple digesting loop; but I haven't found a better way yet. Does one exist? Is there some other way to do this that's a lot prettier?
See the docs for the setdefault() method:
setdefault(key[, default])
If key is
in the dictionary, return its value.
If not, insert key with a value of
default and return default. default
defaults to None.
You can use this as a single call that will get b if it exists, or set b to an empty list if it doesn't already exist - and either way, return b:
>>> key = 'b'
>>> val = 'a'
>>> print d
{}
>>> d.setdefault(key, []).append(val)
>>> print d
{'b': ['a']}
>>> d.setdefault(key, []).append('zee')
>>> print d
{'b': ['a', 'zee']}
Combine this with a simple "not in" check and you've done what you're after in three lines:
>>> b = d.setdefault('b', [])
>>> if val not in b:
... b.append(val)
...
>>> print d
{'b': ['a', 'zee', 'c']}
Assuming you're not really tied to lists, defaultdict and set are quite handy.
import collections
d = collections.defaultdict(set)
for a, b in mappings:
d[b].add(a)
If you really want lists instead of sets, you could follow this with a
for k, v in d.iteritems():
d[k] = list(v)
And if you really want a dict instead of a defaultdict, you can say
d = dict(d)
I don't really see any reason you'd want to, though.
Use collections.defaultdict
your_dict = defaultdict(list)
for (a,b) in your_list:
your_dict[b].append(a)
you can sort your tuples O(n log n) then create your dictionary O(n)
or simplier O(n) but could impose heavy load on memory in case of many tuples:
your_dict = {}
for (a,b) in your_list:
if b in your_dict:
your_dict[b].append(a)
else:
your_dict[b]=[a]
Hmm it's pretty much the same as you've described. What's awkward about that?
You could also consider using an sql database to do the dirty work.
Instead of using an if, AFAIK it is more pythonic to use a try block instead.
your_list=[('a',1),('a',3),('b',1),('f',1),('a',2),('z',1)]
your_dict={}
for (a,b) in your_list:
try:
your_dict[b].append(a)
except KeyError:
your_dict[b]=[a]
print your_dict
I am not sure how you will get out of the key test, but once they key/value pair has been initialized it is easy :)
d = {}
if 'b' not in d:
d['b'] = set()
d['b'].add('a')
The set will ensure that only 1 of 'a' is in the collection. You need to do the initial 'b' check though to make sure the key/value exist.
Dict get method?
It returns the value of my_dict[some_key] if some_key is in the dictionary, and if not - returns some default value ([] in the example below):
my_dict[some_key] = my_dict.get(some_key, []).append(something_else)
There's another way that's rather efficient (though maybe not as efficient as sets) and simple. It's similar in practice to defaultdict but does not require an additional import.
Granted that you have a dict with empty (None) keys, it means you also create the dict keys somewhere. You can do so with the dict.fromkeys method, and this method also allows for setting a default value to all keys.
keylist = ['key1', 'key2']
result = dict.fromkeys(keylist, [])
where result will be:
{'key1': [], 'key2': []}
Then you can do your loop and use result['key1'].append(..) directly