2

I have a data table as below:

      A  B  C
type1 A1 B1 C1
type2 A2 B2 C2

I use the code below

d={}
D={}
h = ['A','B','C']
type1=['A1','B1','C1']
type2=['A2','B2','C2']

for i,val in enumerate(h):

    d['Type1'] = type1[i]
    d['Type2'] = type2[i]
    D[val]=d
    print('loop',i,'\nd:',d,'\nD:',D,'\n\n====')

#print(D)

I would expect to get the following dict as result:

{'A':{'Type1':'A1','Type2':'A2'},'B':{'Type1':'B1','Type2':B2},'C':{'Type1':C1,'Type2':'C2'},}

However, the output is:

{'A': {'Type1': 'C1', 'Type2': 'C2'}, 'B': {'Type1': 'C1', 'Type2': 'C2'}, 'C': {'Type1': 'C1', 'Type2': 'C2'}}

What's the error in my logic?

I can't figure out what's wrong there.

I added a print in the loop.

loop 0 
d: {'Type1': 'A1', 'Type2': 'A2'} 
D: {'A': {'Type1': 'A1', 'Type2': 'A2'}} 

====
loop 1 
d: {'Type1': 'B1', 'Type2': 'B2'} 
D: {'A': {'Type1': 'B1', 'Type2': 'B2'}, 'B': {'Type1': 'B1', 'Type2': 'B2'}} 

====
loop 2 
d: {'Type1': 'C1', 'Type2': 'C2'} 
D: {'A': {'Type1': 'C1', 'Type2': 'C2'}, 'B': {'Type1': 'C1', 'Type2': 'C2'}, 'C': {'Type1': 'C1', 'Type2': 'C2'}} 

====

from the output, the d is correct in every loop But D is getting changed in very loop! Not sure why it would be in that way.

Niuya
  • 428
  • 1
  • 5
  • 14

3 Answers3

6

You are using the same dictionary for every iteration. You should move the definition of d into the loop.

for i,val in enumerate(h):
    d = {}
    ...
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • loop 0 d {'Type1': 'A1', 'Type2': 'A2'} D {'A': {'Type1': 'A1', 'Type2': 'A2'}} loop 1 d {'Type1': 'B1', 'Type2': 'B2'} D {'A': {'Type1': 'B1', 'Type2': 'B2'}, 'B': {'Type1': 'B1', 'Type2': 'B2'}} loop 2 d {'Type1': 'C1', 'Type2': 'C2'} D {'A': {'Type1': 'C1', 'Type2': 'C2'}, 'B': {'Type1': 'C1', 'Type2': 'C2'}, 'C': {'Type1': 'C1', 'Type2': 'C2'}} -- dict d is correct in every loop, but I don't know why D will continous get changed? – Niuya Sep 05 '18 at 07:47
  • I don't understand this comment. Did you move the definition of d into the loop as I suggest? – Daniel Roseman Sep 05 '18 at 08:10
  • your answer do fix the issue itself, but what I need is explanation. from my current understanding, it will use reference first, but if the reference dict is cleared ( d={} ) , it will pass the value to who reference it – Niuya Sep 05 '18 at 08:23
0

because every time of loop, d is not a brandnew dict.

you should let d = {} first in the loop.

pfctgeorge
  • 698
  • 3
  • 9
0

As a disclaimer, I'm no expert on how Python handles these things, so you'll likely get a better and more thorough explanation elsewhere.

With your current code, the line D[val]=d sets D[val] by reference, rather than by value. This means that if d changes, so does D[val]. You can see this from this code:

D={}
h = ['A','B','C']
type1=['A1','B1','C1']
type2=['A2','B2','C2']

d={}

for i,val in enumerate(h):

    d['Type1'] = type1[i]
    d['Type2'] = type2[i]
    D[val]=d

d['Type1'] = 'This is a test'

print(D)
"""
{'A': {'Type1': 'This is a test', 'Type2': 'C2'}, 'B': {'Type1': 'This is a test', 'Type2': 'C2'}, 'C': {'Type1': 'This is a test', 'Type2': 'C2'}}
"""

Changing it to pass a copy of the dictionary would solve the issue, as you're no longer referring to the dictionary itself, which is changing, but instead to a copy of that dictionary, which will not change.

Often in these situations it's best not to use a loop at all. You could, for instance, use a dictionary comprehension. This means you can avoid mutating values through a loop, and instead get only the data you need:

h = ['A','B','C']
type1=['A1','B1','C1']
type2=['A2','B2','C2']

D = {
  val: {
    "Type1": type1[i],
    "Type2": type2[i],
  } for
  (i, val) in
  enumerate(h)
}
OliverRadini
  • 6,238
  • 1
  • 21
  • 46
  • I understand this do fix the issue. I just don't know why the D is getting changed. for first loop, the Key 'A' should be done. then in second loop, it's loop for Key'B', but why key "A" are still changed? I just stuck on this. – Niuya Sep 05 '18 at 08:00
  • I've updated the answer to add a little more detail, though I'm no expert on this – OliverRadini Sep 05 '18 at 08:10
  • OK, you mention the most important thing "reference", and it's the root cause, it didn't get the value. if I change to D[val] = d.copy() , then it's good. so just need to aware of this DICTX[key]= dict_x , it's just reference, not value – Niuya Sep 05 '18 at 08:19
  • 1
    additional comment on my understanding. D will always have mirror change except d={} , in other words, D will always reference d until it's dead :D -- this is interesting thing in Python. – Niuya Sep 05 '18 at 08:27
  • Yes, that's how I understand it. I believe that moving `d` into the loop means that it's no longer passed by reference – OliverRadini Sep 05 '18 at 08:36