Creating a dictionary composed of distinct copies of another dictionary - python

In Python, with two dictionaries, simply doing
dict2 = dict1
Will not cause dict2 to be a distinct copy of dict1. They will point to the same thing, so modifying dict2 will perform the same effect on dict1.
One workaround is
dict2 = dict(dict1)
So if I were to modify dict2, it would not affect dict1's values.
In my program, I'm currently making a dictionary that's composed of multiple copies of a previous dictionary. Let's call the previous dictionary temp2, and the current one temp3. I don't know how many copies I'll need in advance, so I thought of doing this:
temp3 = {}
for i in xrange(some_number):
temp3[i] = dict(temp2)
But my debug tests show that if I modify temp3[0]'s dictionary (which, again, is a copy of temp2), then this will also modify temp3[1]'s copy, and temp3[2], etc., so the result is a dictionary that consists of n identical copies of a dictionary, where n = some_number. Does anyone know of a workaround? Thanks.
EDIT: In response to a comment, temp2 is a dictionary composed of values that are lists, so {a: [list1], b: [list2], etc.}.

Try the copy.deepcopy method: http://docs.python.org/2/library/copy.html

Related

Updating Value in a list of dicts appears to not work

some_key in dictionaty data is a list.
some_key contains a list of dictionaries.
I would like to update the value of one of the keys in this list for each item.
Below is my solution to update the dicts.
data["some_key"] = [some_dict.update({"other_key": some_dict["other_key"].upper()}) for some_dict in data["some_key"]]
But this appears to output:
{'total': 1, 'some_key': [None]}
What am I doing wrong?
dict.update() is an in-place operation, so it returns None to avoid confusion. That means you don't need to modify the list at all:
for some_dict in data["some_key"]:
some_dict.update({"other_key": some_dict["other_key"].upper()})
And with that changed, even dict.update() is not necessary:
some_dict["other_key"] = some_dict["other_key"].upper()

Using locals() to create a list of dictionaries

This might be simple, but I'm stuck. I have globals() that creates dictionaries based on zipping lists (that will differ in sizes, thus differ in the number of the dictionaries that get created). The new dictionaries that get created look like the below:
dict0 = {foo:bar}
dict1 = {more_foo:more_bar}
How do I call these new dictionaries in a for loop?
I want my script to do the below:
for i in (dict0, dict1):
The only issue is that the number of dictx (dictionaries) will differ based on the inputs from the script.
As nicely put in comments, in your case, you should append the dictionaries to a list:
list_iterator = list()
# create dict 1.
list_iterator.append(dict1)
# create dict 2.
list_iterator.append(dict2)
# and so on. If your dict create algorithm is repetetive, you can add the append command to the end.
I figured it out...
for i in range(len(someList)):
dicts = locals()['dict' + str(i)]

can I compare the keys of two dictionaries that are not in the same order?

I apologize this must be a basic question for using dictionaries. I'm learning python, and the objective I have is to compare two dictionaries and recover the Key and Value entries from both entries that are identical. I understand that the order in dictionaries is not relevant like if one is working with a list. But I adopted a code to compare my dictionaries and i just wanted to make sure that the order of the dictionaries does not matter.
The code I have written so far is:
def compare_dict(first,second):
with open('Common_hits_python.txt', 'w') as file:
for keyone in first:
for keytwo in second:
if keytwo == keyone:
if first[keyone] == second[keytwo]:
file.write(keyone + "\t" + first[keyone] + "\n")
Any recommendations would be appreciated. I apologize for the redundany in the code above. But if someone could confirm that comparing two dictionaries this way does not require the key to be in the same order would great. Other ways of writing the function would be really appreciated as well.
Since you loop over both dictionaries and compare all the combinations, no, order doesn't matter. Every key in one dictionary is compared with every key in the other dictionary, eventually.
It is not a very efficient way to test for matching keys, however. Testing if a key is present is as simple as keyone in second, no need to loop over all the keys in second here.
Better still, you can use set intersections instead:
for key, value in first.viewitems() & second.viewitems():
# loops over all key - value pairs that match in both.
file.write('{}\t{}\n'.format(key, value))
This uses dictionary view objects; if you are using Python 3, then you can use first.items() & second.items() as dictionaries there return dictionary views by default.
Using dict.viewitems() as a set only works if the values are hashable too, but since you are treating your values as strings when writing to the file I assumed they were.
If your values are not hashable, you'll need to validate that the values match, but you can still use views and intersect just the keys:
for key in first.viewkeys() & second.viewkeys():
# loops over all keys that match in both.
if first[key] == second[key]:
file.write('{}\t{}\n'.format(key, first[key]))
Again, in Python 3, use first.keys() & second.keys() for the intersection of the two dictionaries by keys.
Your way of doing it is valid. As you look through both lists, the order of the dictionaries does not matter.
You could do this instead, to optimize your code.
for keyone in first:
if keyone in second: # returns true if keyone is present in second.
if first[keyone] == second[keyone]:
file.write(keyone + "\t" + first[keyone] + "\n")
The keys of a dictionary are effectively a set, and Python already has a built-in set type with an efficient intersection method. This will produce a set of keys that are common to both dictionaries:
dict0 = {...}
dict1 = {...}
set0 = set(dict0)
set1 = set(dict1)
keys = set0.intersection(set1)
Your goal is to build a dictionary out of these keys, which can be done with a dictionary comprehension. It will require a condition to keep out the keys that have unequal values in the two original dictionaries:
new_dict = {k: dict0[k] for k in keys if dict0[k] == dict1[k]}
Depending on your intended use for the new dictionary, you might want to copy or deepcopy the old dictionary's values into the new one.

Why are values of a two-level dictionary all pointing to the same object in Python 2.7?

I have tried to define a function to create a two-tiered dictionary, so it should produce the format
dict = {tier1:{tier2:value}}.
The code is:
def two_tier_dict_init(tier1,tier2,value):
dict_name = {}
for t1 in tier1:
dict_name[t1] = {}
for t2 in tier2:
dict_name[t1][t2] = value
return dict_name
So the following example...
tier1 = ["foo","bar"]
tier2 = ["x","y"]
value = []
foobar_dict = two_tier_dict_init(tier1,tier2,value)
on the face of it produces what I want:
foobar_dict = {'foo':{'x': [],'y':[]},
'bar':{'x': [],'y':[]}} }
However, when appending any value like
foobar_dict["foo"]["x"].append("thing")
All values get appended so the result is:
foobar_dict = {'foo':{'x': ["thing"],'y':["thing"]},
'bar':{'x': ["thing"],'y':["thing"]}}
At first I assumed that due to the way my definition builds the dictionary that all values are pointing to the same space in memory, but I could not figure out why this should be the case. I then discovered that if I change the value from an empty list to an integer, when I do the following,
foobar_dict["foo"]["x"] +=1
only the desired value is changed.
I must therefore conclude that it is something to do with the list.append method, but I can not figure it out. What is the explanation?
N.B. I require this function for building large dictionaries of dictionaries where each tier has hundreds of elements. I have also used the same method to build a three-tiered version with the same issue occurring.
You only passed in one list object, and your second-tier dictionary only stored references to that one object.
If you need to store distinct lists, you need to create a new list for each entry. You could use a factory function for that:
def two_tier_dict_init(tier1, tier2, value_factory):
dict_name = {}
for t1 in tier1:
dict_name[t1] = {}
for t2 in tier2:
dict_name[t1][t2] = value_factory()
return dict_name
Then use:
two_tier_dict_init(tier1, tier2, list)
to have it create empty lists. You can use any callable for the value factory here, including a lambda if you want to store an immutable object like a string or an integer:
two_tier_dict_init(tier1, tier2, lambda: "I am shared but immutable")
You could use a dict comprehension to simplify your function:
def two_tier_dict_init(tier1, tier2, value_factory):
return {t1: {t2: value_factory() for t2 in tier2} for t1 in tier1}
It happens because you are filling all second-tier dicts with the same list that you passed as value, and all entries are pointing to the same list object.
One solution is to copy the list at each attribution:
dict_name[t1][t2] = value[:]
This only works if you are sure that value is always a list.
Another, more generic solution, that works with any object, including nested lists and dictionaries, is deep copying:
dict_name[t1][t2] = copy.deepcopy(value)
If you fill the dicts with an immutable object like a number or string, internally all entries would refer to the same object as well, but the undesirable effect would not happen because numbers and strings are immutable.
All the values refer to the same list object. When you call append() on that list object, all of the dictionary values appear to change at the same time.
To create a copy of the list change
dict_name[t1][t2] = value
to
dict_name[t1][t2] = value[:]
or to
dict_name[t1][t2] = copy.deepcopy(value)
The former will make a shallow (i.e. one-level) copy, and the latter will do a deep copy.
The reason this appears to work with ints is because they are immutable, and augmented assignments (+= and friends) do a name rebind just like ordinary assignment statements (it just might be back to the same object). When you do this:
foobar_dict["foo"]["x"] +=1
you end up replacing the old int object with a different one. ints have no capability to change value in-place, so the addition builds (or, possibly finds, since CPython interns certain ints) a different int with the new value.
So even if foobar_dict["foo"]["x"] and foobar_dict["foo"]["y"] started out with the same int (and they did), adding to one of them makes them now contain different ints.
You can see this difference if you try it out with simpler variables:
>>> a = b = 1
>>> a is b
True
>>> a += 1
>>> a
2
>>> b
1
On the other hand, list is mutable, and calling append doesn't do any rebinding. So, as you suspected, if foobar_dict["foo"]["x"] and foobar_dict["foo"]["y"] are the same list (and they are - check this with is), and you append to it, they are still the same list.

Python - can a dict have a value that is a list?

When using Python is it possible that a dict can have a value that is a list?
for example, a dictionary that would look like the following (see KeyName3's values):
{
keyName1 : value1,
keyName2: value2,
keyName3: {val1, val2, val3}
}
I already know that I can use 'defaultdict' however single values are (understandably) returned as a list.
The reason I ask is that my code must be generic so that the caller can retieve single key values as an item (just like from a dict key-value) and not as list (without having to specify pop[0] the list) - however also retrieve multiple values as a list.
If not then any suugestions would be welcome.
If someone can help then that would be great.
Thanks in Advance,
Paul
*I'm using Python 2.6 however writing scripts that must also be forward compatible with Python 3.0+.
Yes. The values in a dict can be any kind of python object. The keys can be any hashable object (which does not allow a list, but does allow a tuple).
You need to use [], not {} to create a list:
{ keyName1 : value1, keyName2: value2, keyName3: [val1, val2, val3] }
Yes, it's possible:
d = {}
d["list key"] = [1,2,3]
print d
output:
{'list key': [1, 2, 3]}
It definitely can have a list and any object as value but the dictionary cannot have a list as key because the list is mutable data structure and keys cannot be mutable else of what use are they.

Categories

Resources