【问题标题】:Why don't Python dictionaries treat keys independently in this script? [duplicate]为什么 Python 字典不在此脚本中独立处理键? [复制]
【发布时间】:2014-06-26 03:13:55
【问题描述】:

我希望我的挫败感会被一些启发所取代 - 这是演示问题的脚本的最小版本:

首先我创建一个字典:

dic  = {
    'foo':{}, 
    'bar':{}
    }

然后我们实例化一个可以迭代追加的模板字典 dic的键:

appendic= {  
    'is':'',       #  '' is a terminal value to be replaced later
}

所以这里我们将appendic 附加到dic 中的每个键:

dic['foo'] = appendic
dic['bar'] = appendic

现在我们用有意义的东西替换终端值'':

dic['foo']['is'] = 'foo'
dic['bar']['is'] = 'bar' 

此时,我的直觉告诉我,如果我们调用:

print(dic['foo']['is']) 我们得到'foo'

但相反,Python 将 'bar' ... 返回给我未经训练的头脑,这是违反直觉的。

问题:

  • 如何告诉 Python 保持 dic 的键独立?
  • 为什么这是默认行为?这有什么用例?

【问题讨论】:

    标签: python dictionary key key-value python-2.6


    【解决方案1】:

    当您将appendic 分配给两个不同的键时,Python 不会进行复制。而是分配一个引用。

    因此,dic['please_make_me_Foo']dic['dont_make_him_Bar'] 都引用了同一个对象。这些不是单独的字典,它们都是同一个对象,appendic 也是引用的对象。

    如果您希望它们是单独的字典,请改为创建 appendic 的副本。 dict.copy() 方法创建字典的浅拷贝:

    dic['please_make_me_Foo']= appendic.copy()
    dic['dont_make_him_Bar'] = appendic.copy()
    

    Shallow 意味着创建了一个新字典,并且复制了对所包含键和值的所有引用。

    如果appendic 本身包含的值也是字典,则不会复制这些值。新副本和appendic 都将引用相同的值。在大多数情况下,这不是问题,因为大多数原始值(字符串、整数等)都是不可变的,并且当您将这些值替换为新值时,您永远不会注意到引用是共享的。

    【讨论】:

    • @hello_there_andy:如果 Python 在您每次存储对它的引用时都会复制该对象,那么对于初学者来说,您将需要更多的内存来运行您的代码。
    • 这必须是至少一百个问题的副本,您应该知道这些问题,因为您可能回答了其中的一半。为什么不关闭这个或至少下一个作为副本呢?一旦我回到电脑而不是移动设备上,我什至会帮助您寻找合适的...
    • @hello_there_andy:这就是 Python 中赋值的工作方式。既然你很惊讶,我建议阅读 Ned Batchelder 的 Facts and myths about Python names and values
    • @l4mpi:我很想找到一个合适的。我们真的需要一个更通用的“停止我的字典/列表/设置/自定义类正在共享”作为欺骗目标的某个地方。
    • @hello_there_andy:我们没想到你会找到骗子;当您还不了解 Python 存储对对象的引用时,肯定很难搜索该行为。 l4mpi 指的是他觉得我应该更了解并发现你是个骗子。 :-)
    【解决方案2】:

    你做一个字典:

    appendic= {  
        'Python_made_me':''
    }
    

    将它添加到您的其他字典两次

    dic['please_make_me_Foo']= appendic
    dic['dont_make_him_Bar'] = appendic
    

    并设置单个dict的Python_made_me两次

    dic['please_make_me_Foo']['Python_made_me'] = 'Foo'
    dic['dont_make_him_Bar']['Python_made_me']  = 'Bar' 
    

    但是因为它们是相同的字典第二行会覆盖第一行

    如果需要复制,需要使用copy方法:

    dic['please_make_me_Foo']= appendic.copy()
    dic['dont_make_him_Bar'] = appendic.copy()
    

    【讨论】:

      【解决方案3】:

      好的,我只是将其写为对其他答案的补充。当您操作字典时,您将 reference 操作到一个实例,这是您错误的根本原因。使用hex(id(foo)),您将获得foo 的内存地址,所以让我们在下面的示例中显示d 实例的地址以使该有形

      >>> hex(id(d))
      '0x10bd95e60'
      >>> hex(id(e[1]))
      '0x10bd95e60'
      >>> hex(id(f[1]))
      '0x10bd95e60'
      

      因此,如果您在e[1] 中添加或删除值,您实际上更改的实例d 所指的实例相同,并且字典是可变的,即您可以更改内的值。

      现在您想知道为什么在处理整数时不会发生这种情况?因为,事实上确实如此,只是整数是不可变的:

      >>> i = 1
      >>> hex(id(i))
      '0x10ba51e90'
      >>> j = i
      >>> hex(id(j))
      '0x10ba51e90'
      >>> i = 2
      >>> hex(id(i))
      '0x10ba51eb0'
      

      即i 指向内存中的另一个地方。

      虽然可以通过使用类来创建一个可变整数:

      >>> class Integer:
      ...   def __init__(self, i):
      ...     self.i = i
      ... 
      >>> i = Integer(2)
      >>> hex(id(i))
      '0x10bd9b410'
      >>> j = i
      >>> hex(id(j))
      '0x10bd9b410'
      >>> j.i = 2
      >>> i.i
      2
      >>> hex(id(i))
      '0x10bd9b410'
      

      为了创建同一个字典的新实例,您需要使用字典的copy() 成员:

      >>> hex(id(d))
      '0x10bd95e60'
      >>> w = d.copy()
      >>> x = d.copy()
      >>> y = d.copy()
      >>> hex(id(w))
      '0x10bd96128'
      >>> hex(id(x))
      '0x10bd95f80'
      >>> hex(id(y))
      '0x10bd96098'
      

      【讨论】:

        【解决方案4】:
        dic['please_make_me_Foo']= appendic
        dic['dont_make_him_Bar'] = appendic
        

        appendic 是一个对象 - 您正在为 dic 中的两个键分配对同一对象的引用。所以当你改变一个,你就改变了两个。

        试试这个:

        dic['please_make_me_Foo']= appendic.copy()
        dic['dont_make_him_Bar'] = appendic.copy()
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-04-12
          • 2019-03-30
          • 2018-02-19
          • 2017-05-20
          • 1970-01-01
          • 1970-01-01
          • 2015-10-25
          • 1970-01-01
          相关资源
          最近更新 更多