0

I try to create a dictionary which will have a relations to different sides taken from the list.

import collections
def create_dict(sides = ["A", "B", "C"]):
    my_dict = dict.fromkeys(sides, {})
    print("my_dict created here: ", my_dict)
    for k in sides:
        remining_list = collections.deque(sides)
        remining_list.remove(k)
        my_dict[k]["relation"] = dict.fromkeys(remining_list, 0)
    return my_dict

The dict is created with empty dictionaries: ('my_dict created here: ', {'A': {}, 'C': {}, 'B': {}}) I expect an output as:

{'A': {'relation': {'B': 0, 'C': 0}},
 'B': {'relation': {'A': 0, 'C': 0}},
 'C': {'relation': {'A': 0, 'B': 0}}}

but instead each value of inner dictionary comes to the last processed dictionary like this:

{'A': {'relation': {'A': 0, 'B': 0}},
 'C': {'relation': {'A': 0, 'B': 0}},
 'B': {'relation': {'A': 0, 'B': 0}}}

Can not figure out in which step I do wrong. When I loop over the keys I give it as a value only to this particular key not to all as in the output. Also is there any more efficient way to create this dictionary?

for completes this is example of call:

if __name__=="__main__":
    my_dict = create_dict()
    print(my_dict)
4
  • 1
    I believe this my_dict = dict.fromkeys(sides, {}) make all keys point to the same dictionary Commented Nov 14, 2019 at 18:50
  • 1
    @DanielMesejo Yes, that's the answer. Commented Nov 14, 2019 at 18:51
  • Duplicate (in spirit) of stackoverflow.com/q/1132941/1126841; here, the mutable default is the value provided by fromkeys, rather than a hard-coded default parameter value. Commented Nov 14, 2019 at 18:53
  • Why are you using a deque? Deques allow efficient mutation at the ends, but remove is still O(n) (because it might have to remove an item from the interior of the deque). There's no benefit over a list here. Commented Nov 14, 2019 at 18:58

2 Answers 2

3

Check the doc on dict.fromkeys. All the keys in the returned dict share the same value. You need a constructor that uses a factory, instead of a value.

Try my_dict = {i:{} for i in sides}.

For the complete solution, I'd use a nested version.

def create_dict(sides):
    return {i:dict(relation={j:0 for j in sides if j!=i}) 
               for i in sides}

Note that if order matters, the above only works on CPython 3.6 or later, or any conformant python 3.7 or later. You could use OrderedDict in earlier versions, but it has performance penalties.

Also note that it is generally considered bad form to use lists or other mutable types in default arguments, so you probably want

def create_dict(sides=("A", "B", "C")):
    ...

instead.

Sign up to request clarification or add additional context in comments.

Comments

0

Since you are using collections already, I suggest you use defaultdict:

import collections


def create_dict(sides=["A", "B", "C"]):
    my_dict = collections.defaultdict(dict)
    for k in sides:
        remining_list = collections.deque(sides)
        remining_list.remove(k)
        my_dict[k]["relation"] = dict.fromkeys(remining_list, 0)
    return dict(my_dict.items())


print(create_dict())

Output

{'A': {'relation': {'B': 0, 'C': 0}}, 'B': {'relation': {'A': 0, 'C': 0}}, 'C': {'relation': {'A': 0, 'B': 0}}}

4 Comments

Don't do defaultdict(lambda: {}) just do defaultdict(dict), base types are 0-argument callables.
@Perkins Nice catch! Updated the answer.
Also, note that there's some overhead in using defaultdict, if all the keys are known ahead of time, it's often better to preallocate them. If I were going to go the defaultdict route, I'd probably make the nested dictionary default to its complete structure defaultdict(lambda: dict(relation=defaultdict(int))) But that's pretty messy.
There's no reason to use collections other than for defaultdict, but it's still a nice idea :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.