19

希望有人可以在这里帮助我。

我对 Python 很陌生,我正在努力找出我做错了什么。

我已经搜索并发现可以链接 Python 变量,以便更改一个变量会更改另一个变量,并且我已经对该id()函数进行了多次测试以掌握这个概念。但我似乎找到了一个例外,我希望有人能解释......

首先,以下工作按预期制作列表的独立副本。

>>> a = [0,0]
>>> b = a[:]
>>> print a is b
False
>>> b[0]=1
>>> print a
[0,0]
>>> print b
[1,0]

但是,如果我稍微改变一下,那么它a就是列表中的列表,它会改变......

>>> a = [[0,0],[0,0]]
>>> b = a[:]
>>> print a is b
False
>>> b[0][0]=1
>>> print a
[[1, 0], [0, 0]]
>>> print b
[[1, 0], [0, 0]]

现在我们看到任何更新b也将适用于a,但是print a is b返回的结果是False?? 我也对此进行了检查id(),一切都说它们彼此独立,但是当我更新一个时,同样适用于另一个?

谁能解释一下这个??

注意我正在运行这些http://labs.codecademy.com/#:workspace,所以我的第一个想法是这只是他们网站上的一个错误,但我不知道?

编辑:

到目前为止,谢谢大家的精彩回答。那太快了!我知道这可能以前被问过,但很难搜索。

由于所有答案都是正确的,我会等一天再打分。拥有最多 +1 的人将获得标记 :)

4

7 回答 7

18

b = a[:]创建 的浅表副本a因此更改其中的可变列表仍然b会影响.a

换句话说,a并且b不要指向同一个列表(这就是为什么a is not b),而是指向两个不同的列表,它们都包含相同的两个列表。您通过更改这些列表之一,b[0][0] = 1该更改显示在a.

你提到你在玩弄id(),所以看看这个:

>>> a = [[0,0],[0,0]]
>>> b = a[:]
>>> id(a)
2917280                    # <----+
>>> id(b)                  #      |----- different!
2771584                    # <----+
>>> id(a[0]), id(a[1])
(2917320, 2917360)         # <----+
>>> id(b[0]), id(b[1])     #      |----- same!
(2917320, 2917360)         # <----+
于 2013-07-05T21:29:37.077 回答
13

您需要对列表进行深度复制a[:]只做一个浅拷贝-见文档

您可以使用copy.deepcopy功能:

>>> import copy
>>> a = [[0,0],[0,0]]
>>> b = copy.deepcopy(a)
>>> b
[[0, 0], [0, 0]]
>>> b[0][0]=1
>>> a
[[0, 0], [0, 0]]
于 2013-07-05T21:29:51.753 回答
6

我相信了解正在发生的事情的最简单方法是使用视觉表示(这种表示的想法不是我的,尽管我喜欢它)。

首先,您必须了解在 python 中只有对对象的引用。对象本身彼此分开生活。例如,列表[0, 1]是一个列表对象,其中包含对 object和 object的引用。参考是某种链接。这与其他语言中的变量不同,因为变量通常是您放置东西的内存位置。在 python 中,“变量”,即标识符,只是对象的“名称”(=reference)。01

为了理解这一点,让我们用一个比喻来描绘物体之间的关系:假设物体是海中的重石,它们通过绳索和钩子(¿)连接在一起。在海面上生活着指代物体的标识符。标识符是防止物体沉入深处的浮标(他们说,海怪(又名垃圾收集器)会摧毁它们)。

例如,我们可以表示这种情况:

a = [0, 1]

用下图:

         ___
        (   )
~~~~~~~~( a )~~~~~~~~
        (___)
 o        ¿      o
          |       O
          |    o
          |
          |
   +------+-------+
   | [  ¿   , ¿ ] |
   +----|-----|---+
        |     |
        |     |
   o    |     |
 O      |     |
        |     |
      +-+-+ +-+-+
      | 0 | | 1 |
      +---+ +---+

 o                 O  o
    )
   (  )             o
  ) )(  )        ( (
 ( (  )( (      ( ) )

如您所见,标识符a 的是,即用绳索链接到列表对象。列表对象有两个插槽,每个插槽都包含一个连接到对象的链接01.

现在,如果我们这样做:

b = a

标识符b将引用相同的对象a

            ___                 ___
           (   )               (   )
~~~~~~~~~~~( a )~~~~~~~~~~~~~~~( b )~~~~~~~~~~~~~~~~
           (___)               (___)
             ¿                   ¿
              \                 /
    o          \               /             o
  o             \             /            o
                -------+-------
     O         |  [ ¿  ,  ¿ ]  |              O
                ----|-----|----
                    |     |
                  +-+-+ +-+-+
         o        | 0 | | 1 |
                  +---+ +---+              o
    O
       o                              O
                                         o


               )
            ) (                    ) (
         ( (   )(               ( (   )
        ( ) ) (  ) (           ( ) ) (  )

相反,当您通过以下方式对, 进行浅表复制a时:

b = a[:]

创建了一个新列表,其元素是对引用的对象的引用的副本,即您制作了绳索的副本,但它们指向相同的元素:a

               ___                 ___
               (   )               (   )
    ~~~~~~~~~~~( a )~~~~~~~~~~~~~~~( b )~~~~~~~~~~~~~~~~
               (___)               (___)
    O            ¿                   ¿               o
                 |                   |
       o         |                   |
                 |                   |
          -------+------       ------+-------
         |  [ ¿  , ¿ ]  |     |  [ ¿ ,  ¿  ] |
          ----|----|----       ----|----|----
              |    |               |    |
               \    \             /    /
                \    \           /    /
                 \    \         /    /            o
    o             \    \       /    /           o
                   \    \     /    /               o
       o            \    \   /    /
                     \    \ /    /             o
   O                  \    X    /
                       \  / \  /
                        \/   \/
                        |     |
                        |     |
                        |     |
                      +-+-+ +-+-+
                      | 0 | | 1 |
                      +---+ +---+



                 )
           (    (                  )      (
     )(     )    )  )           ( (   )    )  )
    (  )   (  ) (  (  (       (  ) ) (  ) (  (  (

由于整数是不可变的,因此使用副本或相同的对象之间没有任何区别,但是当您用可变list的 s替换整数时,您最终会修改对同一对象的引用,因此您会看到行为。

从视觉上看,代码:

a = [[0, 1], [0, 1]]
b = a[:]

结果是:

                ___                 ___
               (   )               (   )
    ~~~~~~~~~~~( a )~~~~~~~~~~~~~~~( b )~~~~~~~~~~~~~~~~
               (___)               (___)
    O            ¿                   ¿               o
                 |                   |
       o         |                   |
                 |                   |
          -------+------       ------+-------
         |  [ ¿  , ¿ ]  |     |  [ ¿ ,  ¿  ] |
          ----|----|----       ----|----|----
              |     \             /     |
              |      \           /      |
              |       \         /       |
              |        \       /        |
              |         \     /         |
              |          \   /          |
              |           \ /           |
              |            X            |
              |           / \           |
              |          /   \          |
              |         /     \         |
              |        /       \        |
              |       /         \       |
              |      /           \      |
              |     |             \     |
              |     |              |    |
         +----+-----+----+   +-----+----+----+
         | [  ¿  ,  ¿  ] |   | [  ¿  ,  ¿  ] |
         +----|-----|----+   +----|-----|----+
               \     \           /     /
                \     \         /     /
                 \     \       /     /
                  \     \     /     /
                   \     \   /     /
                    \     | /     /
                     |    |/     /
                     |    X     /
                     |   / |   /
                     |  /  |  /
                     \ /   \ /
                      Y     Y
                      |     |
                    +-+-+ +-+-+
                    | 0 | | 1 |
                    +---+ +---+


               )
         (    (                  )      (
   )(     )    )  )           ( (   )    )  )
  (  )   (  ) (  (  (       (  ) ) (  ) (  (  (

请注意该列表如何b引用 的相同子列表a。(实现细节:CPython 的字节码编译器会优化文字表达式,以便在两个子列表中使用相同的01对象。对于小整数还涉及一些缓存,但这并不重要。在一般情况下,子列表没有全部共同元素)。

深层副本是避免共享相同对象的副本。

例如,执行后:

import copy
a = [[0, 1], [0, 1]]
b = copy.deepcopy(a)

情况是:

                ___                                              ___
               (   )                                            (   )
    ~~~~~~~~~~~( a )~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~( b )~~~~~~~~~~~~~~~~
               (___)                                            (___)
    O            ¿                                                ¿               o
                 |                                                |
       o         |                                                |
                 |                                                |
          -------+------                                   -------+------
         |  [ ¿  , ¿ ]  |                                 |  [ ¿  , ¿ ]  |
          ----|----|----                                   ----|----|----
              |     \                                          |     \
              |      \                                         |      \
              |       \                                        |       \
              |        \                                       |        \
              |         \                                      |         \
              |          \                                     |          \
              |           \                                    |           \
              |            \                                   |            \
              |             \                                  |             \
              |              \                                 |              \
              |               \                                |               \
              |                \                               |                \
         +----+----------+   +--+------------+            +----+----------+   +--+------------+
         | [  ¿  ,  ¿  ] |   | [  ¿  ,  ¿  ] |            | [  ¿  ,  ¿  ] |   | [  ¿  ,  ¿  ] |
         +----|-----|----+   +----|-----|----+            +----|-----|----+   +----|-----|----+
               \     \           /     /                        \     \           /     /
                \     \         /     /                          \     \         /     /
                 \     \       /     /                            \     \       /     /
                  \     \     /     /                              \     \     /     /
                   \     \   /     /                                \     \   /     /
                    \     | /     /                                  \     | /     /
                     |    |/     /                                    |    |/     /
                     |    X     /                                     |    X     /
                     |   / |   /                                      |   / |   /
                     |  /  |  /                                       |  /  |  /
                     \ /   \ /                                        \ /   \ /
                      Y     Y                                          Y     Y
                      |     |                                          |     |
                    +-+-+ +-+-+                                      +-+-+ +-+-+
                    | 0 | | 1 |                                      | 0 | | 1 |
                    +---+ +---+                                      +---+ +---+






               )                                               )
         (    (                  )      (                (    (                  )      (
   )(     )    )  )           ( (   )    )  )      )(     )    )  )           ( (   )    )  )
  (  )   (  ) (  (  (       (  ) ) (  ) (  (  (   (  )   (  ) (  (  (       (  ) ) (  ) (  (  (

(实际上,它似乎copy.deepcopy足够聪明,可以避免复制不可变的内置对象,例如不可变对象的int, long, tuples 等,因此所有子列表共享相同的0对象1


请注意,这些图表还可以帮助您了解引用计数的工作原理。每条绳索都是一个引用,直到一个对象有一个引用链,该引用链上升到一个浮标(即标识符),它才会保持活动状态。当没有更多的绳索将物体连接到表面的浮标时,物体就会下沉,并被垃圾收集器销毁。

于 2013-07-05T22:50:40.693 回答
4

a是列表的列表。当您这样做时b=a[:],您会创建一个新列表,但会复制元素。不同的列表也是如此b,但元素(子列表)是相同的。

于 2013-07-05T21:30:06.437 回答
3

在这两种情况下,您都会创建一个独立的列表。所以a is b总是假的。

在第一种情况下,您将一些其他值放入其中一个列表中。

在第二种情况下,您的列表都包含相同的值。

就好像你会写

l = []
a = [l, l]
b = [l, l]

a is not b,但它们包含相同的数据。

如果您l现在修改,则此更改通过所有a[0]a[1]b[0]可见b[1]

于 2013-07-05T21:32:00.007 回答
3

a is bFalsea[0] is b[0]True。所以当你改变时,b[0]你必然会改变a[0]

>>> a = [[0,0],[0,0]]
>>> b = a[:]

>>> # a[0] is b[0] 
>>> print a[0] is b[0]
True

>>> a.append('more stuff')
>>> print a
[[0, 0], [0, 0], 'more stuff']
>>> print b
[[0, 0], [0, 0]]
于 2013-07-05T21:38:45.983 回答
0

当您处理列表中的列表时,有一种替代计算量大的 deepcopy 的方法

origvector=[]
    for ind in range(0, len(testvector)):
        origvector.append(testvector[ind][:])

在这个例子中,“testvector”是一个由 n 个向量组成的矩阵,每个项目包含一个三项目列表。像这样:

{0,1,2}{10,20,30}
{3,4,5}{40,50,60}
{6,7,8}{70,80,90}
于 2015-12-03T18:11:02.673 回答