1

I have the following code:

a_dict = dict.fromkeys(['a', 'b', 'c'], [])  
a_dict['a'].append(1)  
print(a_dict)  

which outputs

`{'a': [1], 'c': [1], 'b': [1]}` 

Why has 1 been appended to all the arrays instead of just the one with key a? If I want to only append to the one with key a, how would I do that?

Jérôme
  • 13,328
  • 7
  • 56
  • 106
Zavec
  • 195
  • 2
  • 10
  • Not really an explanation, (which you kind of gave actually.) but this solves the issues - `a_dict['a'] = 1` – SRC Jan 25 '17 at 08:45

3 Answers3

1

The fromkeys() method returns a new dictionary with the given sequence of elements as the keys of the dictionary.

If the value argument is set, each element of the newly created dictionary is set to the provided value.

If the provided value is a mutable object (whose value can be modified) like list, dictionary, etc., when the mutable object is modified, each element of the sequence also gets updated.

This is because, each element is assigned a reference to the same object (points to the same object in the memory).

omri_saadon
  • 10,193
  • 7
  • 33
  • 58
  • Do you have any references for this, or links to read up more on this? I checked the [documentation](https://docs.python.org/2/library/stdtypes.html#dict.fromkeys) and couldn't find anything on this – Jamie Phan Jan 25 '17 at 08:52
  • @K.J.Phan Yes, you can read more in [here](https://www.programiz.com/python-programming/methods/dictionary/fromkeys) – omri_saadon Jan 25 '17 at 08:54
  • @K.J.Phan In general, this is how Python works. Things aren't implicitly copied. – juanpa.arrivillaga Jan 25 '17 at 09:07
  • Yup thanks! Was just curious at a lower level regarding the design, had a look at the [source](https://hg.python.org/cpython/file/c6880edaf6f3/Objects/dictobject.c) and saw that the implementation of `dict_fromkeys(PyObject *cls, PyObject *args)` does indeed insert the values to the keys with a pointer: `insertdict(mp, key, hash, value)`, where `PyObject *value = Py_None;` (defaulted to None) - at least that's what I think it means haha – Jamie Phan Jan 25 '17 at 09:10
0

Aha, seems dict.fromkeys only makes one copy of value, and returns a pointer to it. If it's initialized as a_dict={'a': [], 'b': [], 'c': []} then the code works as expected.

With that known, found a duplicate at dict.fromkeys all point to same list

The answer suggested there:
yet_another_dict = {key: [] for key in ['a', 'b', 'c']}

Community
  • 1
  • 1
Zavec
  • 195
  • 2
  • 10
0
a_dict = dict.fromkeys(['a', 'b', 'c'], [])

This creates a dictionary with keys 'a', 'b', 'c' and for each key the value [].

The issue here is that each key points to the same value: the same empty dict instance [] in memory.

a_dict['a'] and a_dict['b'] are not just equal (evaluating equally), they are the very same instance. The dictionary you created has three keys and the value for each key is a pointer to the same empty dictionary instance.

You can check that:

a_dict['a'] == a_dict['b']
# Returns True: both are equal
a_dict['a'] is a_dict['b']
# Also returns True: both are the same instance

Thus, modifying a_dict['a'] modifies that shared dict instance and affects a_dict['b'].

Jérôme
  • 13,328
  • 7
  • 56
  • 106