【问题标题】:How do I initialize a dictionary of empty lists in Python?dict.fromkeys 都指向同一个列表[重复]
【发布时间】:2018-04-19 09:23:21
【问题描述】:

这让我有点伤心……

我从列表中创建了一个字典

l = ['a','b','c']
d = dict.fromkeys(l, [0,0]) # initializing dictionary with [0,0] as values

d['a'] is d['b'] # returns True

如何使字典的每个值成为单独的列表?如果不遍历所有键并将它们设置为列表,这是否可能?我想修改一个列表而不更改所有其他列表。

【问题讨论】:

    标签: python


    【解决方案1】:

    这个答案是为了向任何因尝试用 fromkeys() 实例化 dict 并在该 dict 中具有可变默认值而感到困惑的人解释这种行为。

    考虑:

    #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
    

    所以对l1 的任何更改都不会影响l2,反之亦然。 到目前为止,这对于任何可变的都是正确的,包括dict

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

    这可能是一个方便的功能。 在这里,我们为每个键分配一个默认值,该值也恰好是一个空列表。

    # 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
    

    确实他们都使用相同的参考! 改变一个就是改变所有,因为它们实际上是同一个对象!

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

    对于许多人来说,这不是预期的!

    现在让我们尝试将列表的显式副本用作默认值。

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

    现在创建一个带有 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']}
    

    还是不开心! 我听到有人喊,那是因为我使用了一个空列表!

    >>> 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']}
    

    fromkeys() 的默认行为是将 None 分配给值。

    >>> 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
    

    事实上,所有的值都是相同的(也是唯一的!)None。 现在,让我们以无数种方式之一迭代dict 并更改值。

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

    嗯。看起来和以前一样!

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

    但它们确实是不同的[]s,在这种情况下,这是预期的结果。

    【讨论】:

    • 所以我们必须迭代?
    • 我认为重点是不要迭代......这是捷径,否则我为什么首先需要这个功能?
    【解决方案2】:
    l = ['a','b','c']
    d = dict((i, [0, 0]) for i in l)
    

    【讨论】:

      【解决方案3】:

      你可以使用字典理解:

      >>> keys = ['a','b','c']
      >>> value = [0, 0]
      >>> {key: list(value) for key in keys}
          {'a': [0, 0], 'b': [0, 0], 'c': [0, 0]}
      

      【讨论】:

      • list(value) 和这里的value[:] 是一样的吗?
      • @Droogans:是的。我只是觉得空切片符号很难看。
      • value[:] 并不丑(除非你和 Alex Martelli 一样审美 :)),而且打字少。在最新版本的 Python 中,现在有一个 list.copy 方法。在性能方面,对于小型列表(最多 50 或 60 个项目),切片是最快的,但对于较大的列表 list(value) 实际上要快一些。 value.copy() 的性能似乎与 list(value) 相似。对于大型列表,所有 3 种技术都会显着减慢速度:在我的旧 32 位机器上,大约 32k,YMMV 取决于 CPU 的字大小和缓存大小。
      【解决方案4】:

      你可以用这个:

      l = ['a', 'b', 'c']
      d = dict((k, [0, 0]) for k in l)
      

      【讨论】:

        猜你喜欢
        • 2020-09-19
        • 1970-01-01
        • 2014-12-18
        • 1970-01-01
        • 2017-04-13
        • 1970-01-01
        • 2020-10-17
        • 1970-01-01
        • 2011-06-24
        相关资源
        最近更新 更多