-2

I have following dictionary,

     result = 
    [{'Img Entropy': 0.4759365334486925,
      'Avg Row Entropy': 0.4756050513785311,
      'Comp Size, B': 9675063,
      'COMP RATIO, out/in': 0.10262087228128054,
      'Stack Pos': 3},
     {'Img Entropy': 0.4759365334486925,
      'Avg Row Entropy': 0.4756050513785311,
      'Comp Size, B': 9675063,
      'COMP RATIO, out/in': 0.10262087228128054,
      'Stack Pos': 3},
     {'Img Entropy': 0.4759365334486925,
      'Avg Row Entropy': 0.4756050513785311,
      'Comp Size, B': 9675063,
      'COMP RATIO, out/in': 0.10262087228128054,
      'Stack Pos': 3}]

I would like to update the value for 2nd last 'Stack Pos'. When I run the following command, all the 'Stack Pos' keys get updated with value 10.

result[-2]['Stack Pos'] = 10

How can I update/add to only the specific key in the list?

The following function creates the list of dictionaries -

def get_compression_stats(img):
    result = []
    meta = {}
    r = range(0,len(img))
    i_e, r_avg_e, r_median_e = get_entropy(img,r)
    #iterate over all combinations for each file
    comp_parameters = {'typesize':4}
    filter_names = ['NONE','SHUFFLE','BITSHUFFLE','BYTEDELTA','SHUFFLE+BYTEDELTA','BITSHUFFLE+BYTEDELTA','SHUFFLE+BITSHUFFLE+BYTEDELTA']
    rows_for_blocksize = [0,1,64]
    for c in blosc2.compressor_list():
        print("Codec: "+str(c))
        comp_parameters['codec'] = c
        for f in filter_names:
            print("Filter: "+f)
            comp_parameters['filters'] = get_filter_array(f)
            for r in rows_for_blocksize:
                comp_parameters['blocksize'] = r*img.shape[1]*img[0][0].nbytes
                print("Blocksize: "+ str(comp_parameters['blocksize']))
                i_u8 = get_ubyte_img(img)
                c_img = blosc2.compress2(i_u8,**comp_parameters)
                orig_len, comp_len, blk_size = blosc2.get_cbuffer_sizes(c_img)
                c_ratio = comp_len/orig_len
                meta['Img Entropy'] = i_e
                meta['Avg Row Entropy'] = r_avg_e
                meta['Comp Size, B'] = comp_len
                meta['COMP RATIO, out/in'] = c_ratio
                print("Comp Ratio, out/in: "+ str(c_ratio))
                result.append(meta)
    return(result)

Thank you.

shparekh
  • 820
  • 10
  • 20
  • 1
    Seems like the problem happens when you create the variable `result`. You are creating three references to the same object, so modifying one of them will modify all of them. – rafaelc May 09 '23 at 00:10
  • 1
    If they all get updated, then it must be that all three entries in the list are actually _the same object_, so of course updating one of them will update all of them. – John Gordon May 09 '23 at 00:11
  • Show us how you are creating `result` in the first place. – John Gordon May 09 '23 at 00:12
  • Updated the question to show how the list of dictionaries is created. – shparekh May 09 '23 at 00:19
  • Does this answer your question? [Changing one dict value changes all values](https://stackoverflow.com/questions/13498453/changing-one-dict-value-changes-all-values) – Pranav Hosangadi May 09 '23 at 00:21
  • You haven't shown us how it is created. `result = get_compression_stats(img)` You'll need to show us that function also. – John Gordon May 09 '23 at 00:25
  • Just did. Removed some (key,value) pairs from the dictionary to avoid clutter. – shparekh May 09 '23 at 00:39

2 Answers2

0

If that happens, your list just contains multiple references to the very same dictionary - and not a list of copies.

So, you need to copy the dict when you make the list, instead of adding it multiple times. The copy module helps.

Marcus Müller
  • 34,677
  • 4
  • 53
  • 94
0

This is caused because dictionaries in Python are pass-by-reference, not pass-by-value, and Python never implicitly copies objects. What happens is Python is given multiple copies of the location to the same dictionary meaning when you change one, it changes all of them.

You need to "recreate" meta each time you run through the for loop. If you put meta = {} at the top of the for loop it will fix it as it is recreating meta as a new empty dictionary.

Additionally, at the bottom of the for loop you can do:

meta = {
  'Img Entropy': i_e,
  'Avg Row Entropy': r_avg_e,
  'Comp Size, B': comp_len,
  'COMP RATIO, out/in': c_ratio,
}
ajgrinds
  • 186
  • 11
  • 1
    Neither `dict1` nor `dict2` are mentioned in the question. This answer is, at best, quite incomplete. – John Gordon May 09 '23 at 00:14
  • 1
    pass-by-reference or value seems nothing to do with this question. What he needs is to traverse, get dict, modify. – Bob liao May 09 '23 at 08:05
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community May 09 '23 at 10:44
  • @Bobliao if the dictionaries were pass by value they wouldn't need to be copied in the same way an int passes by value so if I do `int x = 0; a = x; x = 2;` doesn't change a. – ajgrinds May 09 '23 at 19:04
  • Okay I significantly improved the answer. – ajgrinds May 09 '23 at 19:11
  • @ajgrinds I didn't see the details from the question review page... Just the question description. – Bob liao May 10 '23 at 03:31