dict.fromkeys all point to same list

后端 未结 4 2030
时光说笑
时光说笑 2020-11-22 05:25

This was causing me a bit of grief...

I created a dictionary from a list

l = [\'a\',\'b\',\'c\']
d = dict.fromkeys(l, [0,0]) # initializing dictionar         


        
4条回答
  •  情话喂你
    2020-11-22 05:57

    This answer is here to explain this behavior to anyone flummoxed by the results they get of trying to instantiate a dict with fromkeys() with a mutable default value in that dict.

    Consider:

    #Python 3.4.3 (default, Nov 17 2016, 01:08:31) 
    
    # start by validating that different variables pointing to an
    # empty mutable are indeed different references.
    >>> l1 = []
    >>> l2 = []
    >>> id(l1)
    140150323815176
    >>> id(l2)
    140150324024968
    

    so any change to l1 will not affect l2 and vice versa. this would be true for any mutable so far, including a dict.

    # create a new dict from an iterable of keys
    >>> dict1 = dict.fromkeys(['a', 'b', 'c'], [])
    >>> dict1
    {'c': [], 'b': [], 'a': []}
    

    this can be a handy function. here we are assigning to each key a default value which also happens to be an empty list.

    # the dict has its own id.
    >>> id(dict1)
    140150327601160
    
    # but look at the ids of the values.
    >>> id(dict1['a'])
    140150323816328
    >>> id(dict1['b'])
    140150323816328
    >>> id(dict1['c'])
    140150323816328
    

    Indeed they are all using the same ref! A change to one is a change to all, since they are in fact the same object!

    >>> dict1['a'].append('apples')
    >>> dict1
    {'c': ['apples'], 'b': ['apples'], 'a': ['apples']}
    >>> id(dict1['a'])
    >>> 140150323816328
    >>> id(dict1['b'])
    140150323816328
    >>> id(dict1['c'])
    140150323816328
    

    for many, this was not what was intended!

    Now let's try it with making an explicit copy of the list being used as a the default value.

    >>> empty_list = []
    >>> id(empty_list)
    140150324169864
    

    and now create a dict with a copy of empty_list.

    >>> dict2 = dict.fromkeys(['a', 'b', 'c'], empty_list[:])
    >>> id(dict2)
    140150323831432
    >>> id(dict2['a'])
    140150327184328
    >>> id(dict2['b'])
    140150327184328
    >>> id(dict2['c'])
    140150327184328
    >>> dict2['a'].append('apples')
    >>> dict2
    {'c': ['apples'], 'b': ['apples'], 'a': ['apples']}
    

    Still no joy! I hear someone shout, it's because I used an empty list!

    >>> not_empty_list = [0]
    >>> dict3 = dict.fromkeys(['a', 'b', 'c'], not_empty_list[:])
    >>> dict3
    {'c': [0], 'b': [0], 'a': [0]}
    >>> dict3['a'].append('apples')
    >>> dict3
    {'c': [0, 'apples'], 'b': [0, 'apples'], 'a': [0, 'apples']}
    

    The default behavior of fromkeys() is to assign None to the value.

    >>> dict4 = dict.fromkeys(['a', 'b', 'c'])
    >>> dict4
    {'c': None, 'b': None, 'a': None}
    >>> id(dict4['a'])
    9901984
    >>> id(dict4['b'])
    9901984
    >>> id(dict4['c'])
    9901984
    

    Indeed, all of the values are the same (and the only!) None. Now, let's iterate, in one of a myriad number of ways, through the dict and change the value.

    >>> for k, _ in dict4.items():
    ...    dict4[k] = []
    
    >>> dict4
    {'c': [], 'b': [], 'a': []}
    

    Hmm. Looks the same as before!

    >>> id(dict4['a'])
    140150318876488
    >>> id(dict4['b'])
    140150324122824
    >>> id(dict4['c'])
    140150294277576
    >>> dict4['a'].append('apples')
    >>> dict4
    >>> {'c': [], 'b': [], 'a': ['apples']}
    

    But they are indeed different []s, which was in this case the intended result.

提交回复
热议问题