Independent objects needed when zipping into dict

北战南征 提交于 2020-01-06 05:35:12

问题


I have a list of items:

my_list = ['first', 'second', 'third']

I need to convert this items into a dictionary (so I can access each element by it's name), and associate multiple counters to each element: counter1, counter2, counter3.

So I do the following:

counter_dict = {'counter1': 0, 'counter2': 0, 'counter3': 0}

my_dict = dict(zip(mylist, [counter_dict]*len(mylist)))

This way I obtain a nested dictionary

{
  'first': {
    'counter1': 0,
    'counter2': 0,
    'counter3': 0
  },
  'second': {
    'counter1': 0,
    'counter2': 0,
    'counter3': 0
  },
  'third': {
    'counter1': 0,
    'counter2': 0,
    'counter3': 0
  }
}

The problem here is that each counter dictionary is not independent, so when I update one counter, all of them are updated.

The following line not only updates the counter2 of second but also updates counter2 of first and third

my_dict['second']['counter2'] += 1


{
  'first': {
    'counter1': 0,
    'counter2': 1,
    'counter3': 0
  },
  'second': {
    'counter1': 0,
    'counter2': 1,
    'counter3': 0
  },
  'third': {
    'counter1': 0,
    'counter2': 1,
    'counter3': 0
  }
}

I am aware that by doing [counter_dict]*len(mylist) I am pointing all dictionaries to a single one.

Question is, how can I achieve what I need creating independent dictionaries? At the end I need to:

  • Acces each element of the list as a key
  • For each element have multiple counters that I can update independently

Thanks a lot


回答1:


Try this one:

my_dict = {k: counter_dict.copy() for k in my_list}

Instead of zipping values - it's enough to iterate over keys and copy your counters to values using dict compression.

But note, that this will work only with one level counter_dict. If needed nested dictionary - use deepcopy from copy package:

from copy import deepcopy
my_dict = {k: deepcopy(counter_dict) for k in my_list}

Another aproach - using defaultdict from collections, so you dont even need to create dictionary with predefined counters:

from collections import defaultdict
my_dict = {k: defaultdict(int) for k in my_list}



回答2:


You can use copy.deepcopy to copy a dict and copy its contents.

import copy

Instead of [counter_dict]*len(mylist)
use:

[copy.deepcopy(counter_dict) for _ in mylist]



回答3:


Use a list comprehension, not the * operator, and create a new dictionary for each element of the list.

my_dict = dict(zip(mylist, [dict(counter_dict) for _ in mylist])



回答4:


You can go and make a function that will create a counter_dict whenever you want to assign one:

def create_counter_dict(length):
    return {'counter{}'.format(n+1) : 0 for n in range(length)}

Then when you want to assign a counter_dict to your my_dict:

my_dict = dict(zip(my_list, [create_counter_dict(3) for _ in range(len(my_list))]))

This way you don't have to make a copy of your dictionary, and you can alter this factory function to your needs.



来源:https://stackoverflow.com/questions/46729978/independent-objects-needed-when-zipping-into-dict

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!