dic={}
dic[1]=100
dic[2]=200
dic[1]+=500
here I have initialed a dictionary and I am able to update the key value of the dictionary. But keys in dictionary are immutable, so what's actually happening , can someone please tell?
Just think of it this way. We have an empty dictionary:
d = {}
If we do this:
d[1] = 100
we are simply adding a key and assigning a value to that key, right then and there.
Just like sets, dicts cannot have duplicate keys, so adding another key with the same name will overwrite the original.
Like doing calling d[1] = 200 will overwrite the original d[1].
d[1] += 500 is the same as:
d[1] = d[1]+500
where we are simply telling python to add a key to d called 1, and assign the value of the original key plus 500 to that key.
This question already has answers here:
Accessing dict keys like an attribute?
(31 answers)
Closed 4 years ago.
I can not access the dictionary keys with dot(.) but when I define a class that inherits from dict, I can access its keys using dot(.). Can anybody explain it?
So, when I run this code:
d = {'first_key':1, 'second_key':2}
d.first_key
I get this error:
'dict' object has no attribute 'first_key'
but when I run this:
class DotDict(dict):
pass
d = DotDict()
d.first_key = 1
d.second_key = 2
print(d.first_key)
print(d.second_key)
I get this:
1
2
By applying your example
class DotDict(dict):
pass
d = DotDict()
d.first_key = 1
d.second_key = 2
print(d.first_key)
print(d.second_key)
you set instance parameters first_key and second_key to your DotDict class but not to the dictionary itself. You can see this, if you just put your dictionary content to the screen:
In [5]: d
Out[5]: {}
So, it is just an empty dict. You may access dictionaries the common way:
In [1]: d={}
In [2]: d['first'] = 'foo'
In [3]: d['second'] = 'bar'
In [4]: d
Out[4]: {'first': 'foo', 'second': 'bar'}
in the first case, you are creating keys and values belonging to a dictionary object. while in the second, you are creating attributes of a class which has nothing to do with the dictionary parent class you inherited.
If you want to access the dict elements need to access through keys.
d['first_key']
Output:
1
If you want to access using (.) then use get.
Using get you can get the required value
d = {'first_key':1, 'second_key':2}
print(d.get('first_key'))
Output:
1
With respect to class you are accessing the attributes of class. So you will have to access using (.)
This question already has answers here:
How to copy a dictionary and only edit the copy
(23 answers)
Closed 8 years ago.
I'm expecting my frustration to be overridden with some enlightenment - here's a minimal version of the script to demonstrate the problem:
First I create a dictionary:
dic = {
'foo':{},
'bar':{}
}
Then we instantiate a template dictionary that can be iteratively appended
to keys of dic:
appendic= {
'is':'', # '' is a terminal value to be replaced later
}
So here we append appendic to each of the keys in dic:
dic['foo'] = appendic
dic['bar'] = appendic
Now we replace the terminal values, '', with something meaningful:
dic['foo']['is'] = 'foo'
dic['bar']['is'] = 'bar'
At this point, my intuition tells me that if we call:
print(dic['foo']['is']) we get 'foo'
But instead Python returns 'bar' ... to my un-trained mind that is counter-intuitive.
Questions:
How can I tell Python to keep the keys of dic independent?
Why is this the default behaviour? What use cases does this have?
When you assign a appendic to two different keys, Python doesn't make a copy. It assigns a reference instead.
As a result, both dic['please_make_me_Foo'] and dic['dont_make_him_Bar'] refer to the same object. These are not separate dictionaries, they are both the same object, the one appendic also references to.
If you expected these to be separate dictionaries, create a copy of appendic instead. The dict.copy() method creates a shallow copy of a dictionary:
dic['please_make_me_Foo']= appendic.copy()
dic['dont_make_him_Bar'] = appendic.copy()
Shallow means that a new dictionary is created and all references to keys and values contained are copied over.
If appendic itself contains values that are also dictionaries, these would not be copied. The new copy and appendic would both refer to the same values. In most cases, that's not a problem because most primitive values (strings, integers, etc.) are immutable, and you never notice references are shared as you replace such values with new ones.
You make a dict:
appendic= {
'Python_made_me':''
}
Add it to your other dict twice
dic['please_make_me_Foo']= appendic
dic['dont_make_him_Bar'] = appendic
And set the single dict's Python_made_me value twice
dic['please_make_me_Foo']['Python_made_me'] = 'Foo'
dic['dont_make_him_Bar']['Python_made_me'] = 'Bar'
But because they're the same dict, the second line overwrites the first
If you need to copy it, you need to use the copy method:
dic['please_make_me_Foo']= appendic.copy()
dic['dont_make_him_Bar'] = appendic.copy()
ok, I'm just going to write this as a complement to the other answers. When you manipulate a dictionary, you manipulate the reference to an instance, which is the root cause of your mistake. Using hex(id(foo)) you get the memory address of foo, so let's show the address of d instance in the following example to make that tangible:
>>> hex(id(d))
'0x10bd95e60'
>>> hex(id(e[1]))
'0x10bd95e60'
>>> hex(id(f[1]))
'0x10bd95e60'
so if you add or remove values from e[1], you're actually changing the same instance as the one pointed by d, and as a dictionary is mutable, i.e. you can change values within.
Now you're wondering why that does not happen when you're handling integers? Because, in fact it does, it's just that integers are not mutable:
>>> i = 1
>>> hex(id(i))
'0x10ba51e90'
>>> j = i
>>> hex(id(j))
'0x10ba51e90'
>>> i = 2
>>> hex(id(i))
'0x10ba51eb0'
i.e. i is pointing to another place in the memory.
It's possible to create a mutable integer though, by using a class:
>>> class Integer:
... def __init__(self, i):
... self.i = i
...
>>> i = Integer(2)
>>> hex(id(i))
'0x10bd9b410'
>>> j = i
>>> hex(id(j))
'0x10bd9b410'
>>> j.i = 2
>>> i.i
2
>>> hex(id(i))
'0x10bd9b410'
In order to create a new instance of the same dictionary, you need to use the copy() member of a dict:
>>> hex(id(d))
'0x10bd95e60'
>>> w = d.copy()
>>> x = d.copy()
>>> y = d.copy()
>>> hex(id(w))
'0x10bd96128'
>>> hex(id(x))
'0x10bd95f80'
>>> hex(id(y))
'0x10bd96098'
dic['please_make_me_Foo']= appendic
dic['dont_make_him_Bar'] = appendic
appendic is an object - you are assigning a reference to the same object to both keys in dic. So when you change one, you change both.
Try this instead:
dic['please_make_me_Foo']= appendic.copy()
dic['dont_make_him_Bar'] = appendic.copy()
c = d[s[0]] where c is a new variable, d is a dictionary, and s is a list.
What I'm attempting to do is assign the value of a key that is the same as the first element from my list to c. I know the key is in the dictionary, and that the first element of the list is the same as that key. How do I assign the value of that key to a variable? The code I wrote gives me an out of index error.
However, the code:
a = s[0]
c = d[a]
works.
Why doesn't the first attempt work?
This is for Python 2.7 on windows
I have no idea why it is not working for you. Maybe your list is not populated after all. This should work -
>>> d={'world':'hunger'}
>>> s=['world']
>>> d[s[0]]
'hunger'
>>> c = d[s[0]]
>>> c
'hunger'
>>>
Also Python dictionaries have a nifty feature called has_key. It checks if a key is present in that dict or not. I use this to prevent KeyError Exceptions... You could consider using this to prevent untoward crashes.
Is it possible to assign multiple keys per value in a Python dictionary. One possible solution is to assign value to each key:
dict = {'k1':'v1', 'k2':'v1', 'k3':'v1', 'k4':'v2'}
but this is not memory efficient since my data file is > 2 GB. Otherwise you could make a dictionary of dictionary keys:
key_dic = {'k1':'k1', 'k2':'k1', 'k3':'k1', 'k4':'k4'}
dict = {'k1':'v1', 'k4':'v2'}
main_key = key_dict['k2']
value = dict[main_key]
This is also very time and effort consuming because I have to go through whole dictionary/file twice. Is there any other easy and inbuilt Python solution?
Note: my dictionary values are not simple string (as in the question 'v1', 'v2') rather complex objects (contains different other dictionary/list etc. and not possible to pickle them)
Note: the question seems similar as How can I use both a key and an index for the same dictionary value?
But I am not looking for ordered/indexed dictionary and I am looking for other efficient solutions (if any) other then the two mentioned in this question.
What type are the values?
dict = {'k1':MyClass(1), 'k2':MyClass(1)}
will give duplicate value objects, but
v1 = MyClass(1)
dict = {'k1':v1, 'k2':v1}
results in both keys referring to the same actual object.
In the original question, your values are strings: even though you're declaring the same string twice, I think they'll be interned to the same object in that case
NB. if you're not sure whether you've ended up with duplicates, you can find out like so:
if dict['k1'] is dict['k2']:
print("good: k1 and k2 refer to the same instance")
else:
print("bad: k1 and k2 refer to different instances")
(is check thanks to J.F.Sebastian, replacing id())
Check out this - it's an implementation of exactly what you're asking: multi_key_dict(ionary)
https://pypi.python.org/pypi/multi_key_dict
(sources at https://github.com/formiaczek/python_data_structures/tree/master/multi_key_dict)
(on Unix platforms it possibly comes as a package and you can try to install it with something like:
sudo apt-get install python-multi-key-dict
for Debian, or an equivalent for your distribution)
You can use different types for keys but also keys of the same type. Also you can iterate over items using key types of your choice, e.g.:
m = multi_key_dict()
m['aa', 12] = 12
m['bb', 1] = 'cc and 1'
m['cc', 13] = 'something else'
print m['aa'] # will print '12'
print m[12] # will also print '12'
# but also:
for key, value in m.iteritems(int):
print key, ':', value
# will print:1
# 1 : cc and 1
# 12 : 12
# 13 : something else
# and iterating by string keys:
for key, value in m.iteritems(str):
print key, ':', value
# will print:
# aa : 12
# cc : something else
# bb : cc and 1
m[12] = 20 # now update the value
print m[12] # will print '20' (updated value)
print m['aa'] # will also print '20' (it maps to the same element)
There is no limit to number of keys, so code like:
m['a', 3, 5, 'bb', 33] = 'something'
is valid, and either of keys can be used to refer to so-created value (either to read / write or delete it).
Edit: From version 2.0 it should also work with python3.
Using python 2.7/3 you can combine a tuple, value pair with dictionary comprehension.
keys_values = ( (('k1','k2'), 0), (('k3','k4','k5'), 1) )
d = { key : value for keys, value in keys_values for key in keys }
You can also update the dictionary similarly.
keys_values = ( (('k1',), int), (('k3','k4','k6'), int) )
d.update({ key : value for keys, value in keys_values for key in keys })
I don't think this really gets to the heart of your question but in light of the title, I think this belongs here.
The most straightforward way to do this is to construct your dictionary using the dict.fromkeys() method. It takes a sequence of keys and a value as inputs and then assigns the value to each key.
Your code would be:
dict = dict.fromkeys(['k1', 'k2', 'k3'], 'v1')
dict.update(dict.fromkeys(['k4'], 'v2'))
And the output is:
print(dict)
{'k1': 'v1', 'k2': 'v1', 'k3': 'v1', 'k4': 'v2'}
You can build an auxiliary dictionary of objects that were already created from the parsed data. The key would be the parsed data, the value would be your constructed object -- say the string value should be converted to some specific object. This way you can control when to construct the new object:
existing = {} # auxiliary dictionary for making the duplicates shared
result = {}
for k, v in parsed_data_generator():
obj = existing.setdefault(v, MyClass(v)) # could be made more efficient
result[k] = obj
Then all the result dictionary duplicate value objects will be represented by a single object of the MyClass class. After building the result, the existing auxiliary dictionary can be deleted.
Here the dict.setdefault() may be elegant and brief. But you should test later whether the more talkative solution is not more efficient -- see below. The reason is that MyClass(v) is always created (in the above example) and then thrown away if its duplicate exists:
existing = {} # auxiliary dictionary for making the duplicates shared
result = {}
for k, v in parsed_data_generator():
if v in existing:
obj = existing[v]
else:
obj = MyClass(v)
existing[v] = obj
result[k] = obj
This technique can be used also when v is not converted to anything special. For example, if v is a string, both key and value in the auxiliary dictionary will be of the same value. However, the existence of the dictionary ensures that the object will be shared (which is not always ensured by Python).
I was able to achieve similar functionality using pandas MultiIndex, although in my case the values are scalars:
>>> import numpy
>>> import pandas
>>> keys = [numpy.array(['a', 'b', 'c']), numpy.array([1, 2, 3])]
>>> df = pandas.DataFrame(['val1', 'val2', 'val3'], index=keys)
>>> df.index.names = ['str', 'int']
>>> df.xs('b', axis=0, level='str')
0
int
2 val2
>>> df.xs(3, axis=0, level='int')
0
str
c val3
I'm surprised no one has mentioned using Tuples with dictionaries. This works just fine:
my_dictionary = {}
my_dictionary[('k1', 'k2', 'k3')] = 'v1'
my_dictionary[('k4')] = 'v2'