0

I declared a python dictionary with key and value as multiple array. Is it possible to append an array using the key and value index?

This is the way I initialized the python dictionary cvfoldacc

a = []
b = [] 
c = [] 
d = [] 
e = [] 
f = []
classifiers = [a,b,c,d,e,f]
cvfoldacc = dict.fromkeys(range(2,11), classifiers) 

And the result of this initialization is as follows:

cvfoldacc>>

{2: [[], [], [], [], [], []],
 3: [[], [], [], [], [], []],
 4: [[], [], [], [], [], []],
 5: [[], [], [], [], [], []],
 6: [[], [], [], [], [], []],
 7: [[], [], [], [], [], []],
 8: [[], [], [], [], [], []],
 9: [[], [], [], [], [], []],
 10: [[], [], [], [], [], []]}

When I tried to append the first list of key(2) with the code cvfoldacc[2][0].append(8), I am getting the result as:

{2: [[8], [], [], [], [], []],
 3: [[8], [], [], [], [], []],
 4: [[8], [], [], [], [], []],
 5: [[8], [], [], [], [], []],
 6: [[8], [], [], [], [], []],
 7: [[8], [], [], [], [], []],
 8: [[8], [], [], [], [], []],
 9: [[8], [], [], [], [], []],
 10: [[8], [], [], [], [], []]}

But the expected answer should be this:

{2: [[8], [], [], [], [], []],
 3: [[], [], [], [], [], []],
 4: [[], [], [], [], [], []],
 5: [[], [], [], [], [], []],
 6: [[], [], [], [], [], []],
 7: [[], [], [], [], [], []],
 8: [[], [], [], [], [], []],
 9: [[], [], [], [], [], []],
 10: [[], [], [], [], [], []]}

2 Answers2

2

This is the same old deep copy vs shallow copy thing. Try this:

>>> a = []
>>> b = [] 
>>> c = [] 
>>> d = [] 
>>> e = [] 
>>> f = []
>>> classifiers = [a,b,c,d,e,f]
>>> import copy
>>> cvfoldacc = {k:copy.deepcopy(classifiers) for k in range(2,11)}
>>> cvfoldacc[2][0].append(8)
>>> cvfoldacc 
{2: [[8], [], [], [], [], []],
 3: [[], [], [], [], [], []],
 4: [[], [], [], [], [], []],
 5: [[], [], [], [], [], []],
 6: [[], [], [], [], [], []],
 7: [[], [], [], [], [], []],
 8: [[], [], [], [], [], []],
 9: [[], [], [], [], [], []],
 10: [[], [], [], [], [], []]}

You were doing:

a = []
b = [] 
c = [] 
d = [] 
e = [] 
f = []
classifiers = [a,b,c,d,e,f]
cvfoldacc = dict.fromkeys(range(2,11), classifiers) 

Now, the dictionary that is getting created, has the same list in every key, not only they look same, those are the exact same objects, having same identity (memory location in CPython). Let's see:

>>> id(cvfoldacc[2])
171760008
>>> id(cvfoldacc[3])
171760008

And these will be same for other values as well. So dict.fromkeys() assigns the same value to all the keys in the dictionary, does not create a copy of it.

Now, another way, where you make a shallow copy, and this issue would be solved:

>>> cvfoldacc = {a:classifiers.copy() for a in range(2,11)}
>>> id(cvfoldacc[2])
171840616
>>> id(cvfoldacc[3])
171847688

Solved right?

>>> cvfoldacc[2][0].append(8)
{2: [[8], [], [], [], [], []],
 3: [[8], [], [], [], [], []],
 4: [[8], [], [], [], [], []],
 5: [[8], [], [], [], [], []],
 6: [[8], [], [], [], [], []],
 7: [[8], [], [], [], [], []],
 8: [[8], [], [], [], [], []],
 9: [[8], [], [], [], [], []],
 10: [[8], [], [], [], [], []]}

Apparently not!! Let's now look DEEPER. Let's look at the ids of the lists inside the lists:

>>> id(cvfoldacc[2][0])
171810120
>>> id(cvfoldacc[3][0])
171810120

So even though list.copy() created a copy of the outerlist, the inner lists are the same. So essentially you are appending in list: a which is present in all the keys so everything is getting modified.

Deepcopy copied all the objects recursively, avoiding the issue.

Sayandip Dutta
  • 15,602
  • 4
  • 23
  • 52
  • Could you please explain why the previous method did'nt worked.. – user2159982 Nov 12 '19 at 06:28
  • Sure, it will take a little time to write it up. – Sayandip Dutta Nov 12 '19 at 06:29
  • do ```[id(cvfoldacc[x]) for x in range(2,11)]``` for your original code. You'll see they are all the same ID - so you only made references to one list. Now do ```id(classifiers)```. Make a change like ```classifiers[0] = [1,2]```. See how it propagates through your dict? That means you made a dict whose values are all references to that original list. This is particularly an issue with any nested sequence. So you always need to make sure you're instantiating new objects in this situation. – neutrino_logic Nov 12 '19 at 06:38
  • A useful information about the referencing of lists. Thanks – user2159982 Nov 12 '19 at 06:43
  • Glad to help. Also note, the other answer does the same thing, without importing `copy`. Make sure you understand the innerworkings. `{k:[x[:] for x in classifiers] for k in range(2,11)}` here `x[:]` serves as `deepcopy` as it creates a new copy of `a,b,c,d,e,f` in every iteration. – Sayandip Dutta Nov 12 '19 at 06:45
1

You need to create new lists for values of each key like,

>>> a = []
>>> b = [] 
>>> c = [] 
>>> d = [] 
>>> e = [] 
>>> f = []
>>> classifiers = [a,b,c,d,e,f] 
>>> 
>>> d = {k:[x[:] for x in classifiers] for k in range(2,11)} # note i am creating a copy using the `list[:]` notation.
# either the comprehension or `k: copy.deepcopy(classifiers)` is fine
>>> d
{2: [[], [], [], [], [], []], 3: [[], [], [], [], [], []], 4: [[], [], [], [], [], []], 5: [[], [], [], [], [], []], 6: [[], [], [], [], [], []], 7: [[], [], [], [], [], []], 8: [[], [], [], [], [], []], 9: [[], [], [], [], [], []], 10: [[], [], [], [], [], []]}
>>> d[2][0].append(1)
>>> d
{2: [[1], [], [], [], [], []], 3: [[], [], [], [], [], []], 4: [[], [], [], [], [], []], 5: [[], [], [], [], [], []], 6: [[], [], [], [], [], []], 7: [[], [], [], [], [], []], 8: [[], [], [], [], [], []], 9: [[], [], [], [], [], []], 10: [[], [], [], [], [], []]}
han solo
  • 6,390
  • 1
  • 15
  • 19