【问题标题】:Python: two objects are the same [duplicate]Python:两个对象相同[重复]
【发布时间】:2012-09-23 05:58:19
【问题描述】:

可能重复:
Assignment of objects and fundamental types

a = [1,2,3]
b = a
print b is a

此代码打印 True。为什么? "is" 仅在两个变量指向同一个对象时才返回 True,在这种情况下,它们是具有相同值的不同对象。 "==" 会返回 True,但 "is" 不应该。

然而,因为

b.reverse()
print a,b

prints [3, 2, 1] [3, 2, 1],似乎就解释器而言,它们是同一个对象,对b的操作会自动在a上执行。再说一遍,为什么?我以前从未见过这样的事情发生。

【问题讨论】:

  • “当在这种情况下它们是具有相同值的不同对象时”我认为不是。
  • 是时候更好地学习Python了。 a 是对列表 [1,2,3] 的引用,b=aa 中包含的引用分配给 b,因此现在它们包含对同一列表的引用。

标签: python list


【解决方案1】:

它们实际上引用的是同一个对象。

试试这个:

a = [1,2,3]
b = a
print b is a
b[0] = 0
print b is a

您会看到 a 和 b 都已更改,并且彼此仍然相同。

【讨论】:

    【解决方案2】:
    a = [1,2,3]
    b = a
    print b is a
    

    您正在比较对同一 list 的引用。如果您执行以下操作:

    a = [1,2,3]
    b = [1,2,3]
    print b is a
    

    你应该得到一个 False。

    【讨论】:

      【解决方案3】:
      a = [81, 82, 83]
      b = a
      print(a is b) #prints True
      

      这就是这里实际发生的情况:

      对于类似的东西:

      a = [81,82,83]
      b = [81,82,83]
      
      print(a is b) # False
      
      print(a == b)  #True, as == only checks value equality
      

      In [24]: import sys
      
      In [25]: a=[1,2,3]
      
      In [26]: sys.getrefcount(a) #number of references to [1,2,3] are 2
      Out[26]: 2
      
      In [27]: b=a       #now b also points to [1,2,3]
      
      In [28]: sys.getrefcount(a)   # reference to [1,2,3] got increased by 1,
                                    # as b now also points to [1,2,3]
      Out[28]: 3
      
      In [29]: id(a)
      Out[29]: 158656524      #both have the same id(), that's why "b is a" is True
      
      In [30]: id(b)
      Out[30]: 158656524
      

      何时使用copy 模块:

      In [1]: a=[1,2,3]
      
      In [2]: b=a
      
      In [3]: id(a),id(b)        
      Out[3]: (143186380, 143186380)   #both point to the same object
      
      In [4]: b=a[:]                #now use slicing, it is equivalent to b=copy.copy(a)
                                    # or b= list(a)    
      
      In [5]: id(a),id(b)
      Out[5]: (143186380, 143185260)     #as expected both now point to different objects
                                         # so now changing one will not affect other
      
      In [6]: a=[[1,2],[3,4]]          #list of lists
      
      In [7]: b=a[:]                   #use slicing
      
      In [8]: id(a),id(b)            #now both point to different object as expected
                                     # But what about the internal lists?
      Out[8]: (143184492, 143186380)
      
      In [11]: [(id(x),id(y)) for (x,y) in zip(a,b)]   #so internal list are still same objects
                                                       #so doing a[0][3]=5, will changes b[0] too
      Out[11]: [(143185036, 143185036), (143167244, 143167244)]
      
      In [12]: from copy import deepcopy            #to fix that use deepcopy
      
      In [13]: b=deepcopy(a)
      
      In [14]: [(id(x),id(y)) for (x,y) in zip(a,b)]    #now internal lists are different too
      Out[14]: [(143185036, 143167052), (143167244, 143166924)]
      

      更多详情:

      In [32]: def func():
         ....:     a=[1,2,3]
         ....:     b=a
         ....:     
         ....:     
      
      In [34]: import dis
      
      In [35]: dis.dis(func)
        2           0 LOAD_CONST               1 (1)
                    3 LOAD_CONST               2 (2)
                    6 LOAD_CONST               3 (3)
                    9 BUILD_LIST               3
                   12 STORE_FAST               0 (a)   #now 'a' poits to [1,2,3]
      
        3          15 LOAD_FAST                0 (a)    #load the object referenced by a
                   18 STORE_FAST               1 (b)    #store the object returned by a to b 
                   21 LOAD_CONST               0 (None)
                   24 RETURN_VALUE        
      
      In [36]: def func1():
         ....:     a=[1,2,3]
         ....:     b=[1,2,3]
         ....:     
         ....:     
      
      In [37]: dis.dis(func1)      #here both a and b are loaded separately
        2           0 LOAD_CONST               1 (1)
                    3 LOAD_CONST               2 (2)
                    6 LOAD_CONST               3 (3)
                    9 BUILD_LIST               3
                   12 STORE_FAST               0 (a)
      
        3          15 LOAD_CONST               1 (1)
                   18 LOAD_CONST               2 (2)
                   21 LOAD_CONST               3 (3)
                   24 BUILD_LIST               3
                   27 STORE_FAST               1 (b)
                   30 LOAD_CONST               0 (None)
                   33 RETURN_VALUE   
      

      【讨论】:

      • 感谢您的快速回复。我尝试了 a = [1,2,3] b = [1,2,3] 并修复了它,但这不适用于我要编写的代码。如何让解释器知道我希望它考虑 a 和 b 不同的对象,以便 b.reverse() 不会改变 a?
      • @JeffKonecki b=a[:](浅拷贝)
      • @JeffKonecki b=a[:] 将用于简单列表,但对于列表列表,您需要来自copy 模块的deepcopy()
      • 你需要副本做什么?你真正想做什么,为什么不问这个问题呢?
      【解决方案4】:

      我不确定列表是否同样有效,但请查看关于浅拷贝和深拷贝的 numpy.array() 教程:http://www.scipy.org/Tentative_NumPy_Tutorial#head-1529ae93dd5d431ffe3a1001a4ab1a394e70a5f2

      a = b 只是创建对同一对象的新引用。要获得真正的副本,您可能会发现列表对象与链接中的深层副本示例类似b = a.copy()。那么你可以说有 2 个引用指向具有相同值的两个独立对象。

      另外我认为大多数 OO 语言都是这样工作的,= 只是创建一个新引用而不是一个新对象。

      【讨论】:

        【解决方案5】:

        当您执行a = [1, 2, 3] 时,您将名称a 绑定到列表对象。当您执行b = a 时,您将名称b 绑定到a 是什么——在本例中是列表对象。因此,它们是相同的......一个对象可以有多个名称。 Python Data Model 值得一读。

        如果您想复制您的 listobj,那么您可以查看 b = a[:] 以使用 slice 创建浅拷贝,或查看 copy.copy 以获得浅拷贝(应该适用于任意对象)或 @987654329 @ 奇怪的是 - 一个深拷贝。

        您还会注意到在 CPython 中缓存短字符串/小整数的一些令人惊讶的事情...

        >>> a = 4534534
        >>> b = a
        >>> a is b
        True
        >>> b = 4534534
        >>> a is b
        False
        >>> a = 1
        >>> b = a
        >>> a is b
        True
        >>> b = 1
        >>> a is b
        True
        

        【讨论】:

        • 你能解释一下为什么最后一种情况是真的吗?它似乎不一致,那么为什么要这样设计呢?在我看来,仅适用于整数 btw(包括)-5 到 256,这是一个奇数区间。
        • @dan 它也适用于单个字符(部分原因是 256)。由于字符串和整数是不可变的,因此只保留一份常用值的副本是有意义的...... -5 可能是处理一些常见的错误代码,0 和 1 经常使用,对于短循环或按位运算,0..256 有意义...
        【解决方案6】:

        此代码打印 True。为什么?

        因为b is a

        "is" 仅在两个变量指向同一个对象时才返回 True

        如果他们命名相同的对象。 “指向”是一种粗俗的术语,暗示了一种低得多的编程模型。

        在这种情况下,它们是具有相同值的不同对象。

        不,他们不是。

        在 Python 中,b = a 的意思是“b 将不再是它当前名称的名称,如果有的话,并成为任何 a 当前名称的名称”。同一个对象。不是副本。

        在 Python 中不会隐式复制内容。

        prints [3, 2, 1] [3, 2, 1],似乎就解释器而言,它们是同一个对象

        因为他们是。

        对 b 的操作会自动在 a 上执行。

        因为它们是同一个对象。

        再说一遍,为什么?

        再次,因为他们是。

        ...就好像你想到了每一个明显的测试来确认你的行为,但是一旦每一个测试都与你的核心假设相矛盾,你就拒绝拒绝你的核心假设,即使文献中没有任何东西支持你的核心假设(因为事实上它是假的)。

        我以前从未见过这样的事情发生。

        那么你一定从来没有在 Python 中测试过这样的东西,因为它在 Python 中一直都是这样工作的。在编程语言中甚至没有那么奇怪。 Java 对所有非原始类型的东西都做同样的事情,而 C# 对类(引用类型)做同样的事情,同时对结构(值类型)做你显然期望的事情。它被称为“引用语义”,这绝不是一个新想法。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-11-19
          • 2018-05-18
          • 2016-03-30
          • 1970-01-01
          • 1970-01-01
          • 2023-03-15
          相关资源
          最近更新 更多