希望有人可以帮助我.
我是Python的新手,我正在努力弄清楚我做错了什么.
我已经搜索并发现Python变量可以链接,以便更改一个更改另一个,并且我已经使用该id()函数进行了大量测试以掌握这个概念.但我似乎找到了一个例外,我希望有人可以解释......
首先,以下工作按预期方式制作列表的独立副本.
>>> a = [0,0]
>>> b = a[:]
>>> print a is b
False
>>> b[0]=1
>>> print a
[0,0]
>>> print b
[1,0]
Run Code Online (Sandbox Code Playgroud)
但是,如果我稍微改变这一点,那么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]]
Run Code Online (Sandbox Code Playgroud)
现在我们看到任何更新b也将适用于a,但是print a is b返回的结果False?我也反对检查这一点id(),一切都说它们是相互独立的,但是当我更新一个同样适用于另一个??
谁能解释一下这个?
注意到我正在运行这些http://labs.codecademy.com/#:workspace,所以我首先想到的是,这只是他们网站上的一个错误,但我不知道?
编辑:
感谢你们迄今为止的伟大答案.那很快!我知道之前可能已经提出这个问题,但搜索起来很困难.
由于所有答案都是正确的,我会在标记前等一天.谁拥有最多+ 1的人将得到标记:)
ars*_*jii 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) # <----+
Run Code Online (Sandbox Code Playgroud)
Roh*_*ain 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]]
Run Code Online (Sandbox Code Playgroud)
我相信获得正在发生的事情的最简单方法是使用视觉表示(这种表示的想法不是我的,尽管我喜欢它).
首先,您必须了解在python中只有对象的引用.物体本身彼此独立存在.例如,列表[0, 1]是一个列表对象,其中包含对对象0和对象的引用1.引用是某种链接.这与其他语言中的变量不同,因为变量通常是放置内容的内存位置.在python中,"变量",即标识符,只是对象的"名称"(=引用).
为了理解这一点,让我们用隐喻来描绘物体之间的关系:假设物体是海中的重石,它们通过绳索和钩子连接在一起(¿).在海面上居住着引用物体的标识符.标识符是防止物体在深处下沉的浮标(他们说,海怪(也就是垃圾收集器)会破坏它们).
例如,我们可以代表这种情况:
a = [0, 1]
Run Code Online (Sandbox Code Playgroud)
使用下图:
___
( )
~~~~~~~~( a )~~~~~~~~
(___)
o ¿ o
| O
| o
|
|
+------+-------+
| [ ¿ , ¿ ] |
+----|-----|---+
| |
| |
o | |
O | |
| |
+-+-+ +-+-+
| 0 | | 1 |
+---+ +---+
o O o
)
( ) o
) )( ) ( (
( ( )( ( ( ) )
Run Code Online (Sandbox Code Playgroud)
正如您所看到的那样,标识符a 指向列表对象,即与绳索链接.列表对象具有两个槽,每个包含连接到对象的链接0和1.
现在,如果我们这样做:
b = a
Run Code Online (Sandbox Code Playgroud)
该标识符b将引用相同的对象的a:
___ ___
( ) ( )
~~~~~~~~~~~( a )~~~~~~~~~~~~~~~( b )~~~~~~~~~~~~~~~~
(___) (___)
¿ ¿
\ /
o \ / o
o \ / o
-------+-------
O | [ ¿ , ¿ ] | O
----|-----|----
| |
+-+-+ +-+-+
o | 0 | | 1 |
+---+ +---+ o
O
o O
o
)
) ( ) (
( ( )( ( ( )
( ) ) ( ) ( ( ) ) ( )
Run Code Online (Sandbox Code Playgroud)
相反,当你做一个浅的副本时a,通过:
b = a[:]
Run Code Online (Sandbox Code Playgroud)
创建一个新列表,其元素是对所引用对象的引用的副本,即您创建了绳索的副本,但它们指向相同的元素:a
___ ___
( ) ( )
~~~~~~~~~~~( a )~~~~~~~~~~~~~~~( b )~~~~~~~~~~~~~~~~
(___) (___)
O ¿ ¿ o
| |
o | |
| |
-------+------ ------+-------
| [ ¿ , ¿ ] | | [ ¿ , ¿ ] |
----|----|---- ----|----|----
| | | |
\ \ / /
\ \ / /
\ \ / / o
o \ \ / / o
\ \ / / o
o \ \ / /
\ \ / / o
O \ X /
\ / \ /
\/ \/
| |
| |
| |
+-+-+ +-+-+
| 0 | | 1 |
+---+ +---+
)
( ( ) (
)( ) ) ) ( ( ) ) )
( ) ( ) ( ( ( ( ) ) ( ) ( ( (
Run Code Online (Sandbox Code Playgroud)
由于整数是不可变的,因此使用副本或相同的相同对象之间没有任何区别,但是当用可变的list s 替换整数时,最终会修改对同一对象的引用,因此您看到的行为也是如此.
在视觉上,代码:
a = [[0, 1], [0, 1]]
b = a[:]
Run Code Online (Sandbox Code Playgroud)
结果是:
___ ___
( ) ( )
~~~~~~~~~~~( a )~~~~~~~~~~~~~~~( b )~~~~~~~~~~~~~~~~
(___) (___)
O ¿ ¿ o
| |
o | |
| |
-------+------ ------+-------
| [ ¿ , ¿ ] | | [ ¿ , ¿ ] |
----|----|---- ----|----|----
| \ / |
| \ / |
| \ / |
| \ / |
| \ / |
| \ / |
| \ / |
| X |
| / \ |
| / \ |
| / \ |
| / \ |
| / \ |
| / \ |
| | \ |
| | | |
+----+-----+----+ +-----+----+----+
| [ ¿ , ¿ ] | | [ ¿ , ¿ ] |
+----|-----|----+ +----|-----|----+
\ \ / /
\ \ / /
\ \ / /
\ \ / /
\ \ / /
\ | / /
| |/ /
| X /
| / | /
| / | /
\ / \ /
Y Y
| |
+-+-+ +-+-+
| 0 | | 1 |
+---+ +---+
)
( ( ) (
)( ) ) ) ( ( ) ) )
( ) ( ) ( ( ( ( ) ) ( ) ( ( (
Run Code Online (Sandbox Code Playgroud)
请注意列表如何b引用相同的子列表a.(实现细节:CPython的字节码编译器将优化文字表达式,以便在两个子列表中使用相同的0和1对象.小整数也涉及一些缓存,但这并不重要.在一般情况下,子列表并不全部共同的元素).
深层副本是避免共享相同对象的副本.
例如,执行后:
import copy
a = [[0, 1], [0, 1]]
b = copy.deepcopy(a)
Run Code Online (Sandbox Code Playgroud)
情况是:
___ ___
( ) ( )
~~~~~~~~~~~( a )~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~( b )~~~~~~~~~~~~~~~~
(___) (___)
O ¿ ¿ o
| |
o | |
| |
-------+------ -------+------
| [ ¿ , ¿ ] | | [ ¿ , ¿ ] |
----|----|---- ----|----|----
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
+----+----------+ +--+------------+ +----+----------+ +--+------------+
| [ ¿ , ¿ ] | | [ ¿ , ¿ ] | | [ ¿ , ¿ ] | | [ ¿ , ¿ ] |
+----|-----|----+ +----|-----|----+ +----|-----|----+ +----|-----|----+
\ \ / / \ \ / /
\ \ / / \ \ / /
\ \ / / \ \ / /
\ \ / / \ \ / /
\ \ / / \ \ / /
\ | / / \ | / /
| |/ / | |/ /
| X / | X /
| / | / | / | /
| / | / | / | /
\ / \ / \ / \ /
Y Y Y Y
| | | |
+-+-+ +-+-+ +-+-+ +-+-+
| 0 | | 1 | | 0 | | 1 |
+---+ +---+ +---+ +---+
) )
( ( ) ( ( ( ) (
)( ) ) ) ( ( ) ) ) )( ) ) ) ( ( ) ) )
( ) ( ) ( ( ( ( ) ) ( ) ( ( ( ( ) ( ) ( ( ( ( ) ) ( ) ( ( (
Run Code Online (Sandbox Code Playgroud)
(事实上,它似乎是copy.deepcopy足够聪明,避免复制内置是不可变对象,如int,long,tuple不可变对象的等等这么所有子列表共享相同0和1对象)
请注意,这些图表也可以帮助您了解引用计数的工作原理.每根绳子都是一个参考,直到一个物体有一个参考链连接到一个浮标(即一个标识符),它才能保持活着状态.当没有更多的绳索将物体链接到地面的浮标时,物体就会下沉,并被垃圾收集器摧毁.
| 归档时间: |
|
| 查看次数: |
507 次 |
| 最近记录: |