希望有人可以帮助我。
我对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的人将获得该标记:)
答案 0 :(得分:18)
b = a[:]
创建了a
b
,因此更改a
中的可变列表仍会影响{{1}中那些相同的列表 }。
换句话说,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) # <----+
答案 1 :(得分:13)
您需要制作列表的 deepcopy 。 a[:]
仅制作浅色副本 - see docs
您可以使用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]]
答案 2 :(得分:6)
我认为获得正在发生的事情的最简单方法是使用视觉表示(这种表示的想法不是我的,尽管我喜欢它。)
首先,您必须了解在python中只有引用到对象。
物体本身彼此独立存在。例如,列表[0, 1]
是一个列表对象,其中包含对象0
和对象1
的引用。
引用是某种链接。这与其他语言中的变量不同,因为变量通常是放置内容的内存位置。在python中,“变量”,即标识符,只是对象的“名称”(=引用)。
要理解这一点,让我们用隐喻来描绘对象之间的关系: 假设物体是海中沉重的岩石,通过绳索和钩子连接在一起(¿)。 在海面上居住着引用物体的标识符。标识符是防止物体在深处下沉的浮标(他们说,海怪(也就是垃圾收集器)会破坏它们)。
例如,我们可以代表这种情况:
a = [0, 1]
使用下图:
___
( )
~~~~~~~~( a )~~~~~~~~
(___)
o ¿ o
| O
| o
|
|
+------+-------+
| [ ¿ , ¿ ] |
+----|-----|---+
| |
| |
o | |
O | |
| |
+-+-+ +-+-+
| 0 | | 1 |
+---+ +---+
o O o
)
( ) o
) )( ) ( (
( ( )( ( ( ) )
正如您所看到的,标识符a
引用,即与绳索链接,与列表对象相关联。
list-object有两个插槽,每个插槽包含一个连接到对象0
和1
的链接。
现在,如果我们这样做了:
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 |
+---+ +---+
)
( ( ) (
)( ) ) ) ( ( ) ) )
( ) ( ) ( ( ( ( ) ) ( ) ( ( (
由于整数是不可变的,因此使用副本或相同的相同对象之间没有任何区别,但是当您使用{em> mutable 的list
替换整数时,结束修改对同一对象的引用,从而看到你看到的行为。
视觉上,代码:
a = [[0, 1], [0, 1]]
b = a[:]
结果:
___ ___
( ) ( )
~~~~~~~~~~~( a )~~~~~~~~~~~~~~~( b )~~~~~~~~~~~~~~~~
(___) (___)
O ¿ ¿ o
| |
o | |
| |
-------+------ ------+-------
| [ ¿ , ¿ ] | | [ ¿ , ¿ ] |
----|----|---- ----|----|----
| \ / |
| \ / |
| \ / |
| \ / |
| \ / |
| \ / |
| \ / |
| X |
| / \ |
| / \ |
| / \ |
| / \ |
| / \ |
| / \ |
| | \ |
| | | |
+----+-----+----+ +-----+----+----+
| [ ¿ , ¿ ] | | [ ¿ , ¿ ] |
+----|-----|----+ +----|-----|----+
\ \ / /
\ \ / /
\ \ / /
\ \ / /
\ \ / /
\ | / /
| |/ /
| X /
| / | /
| / | /
\ / \ /
Y Y
| |
+-+-+ +-+-+
| 0 | | 1 |
+---+ +---+
)
( ( ) (
)( ) ) ) ( ( ) ) )
( ) ( ) ( ( ( ( ) ) ( ) ( ( (
请注意列表b
如何引用a
的相同子列表。
(实现细节:CPython的字节码编译器将优化文字表达式,以便在两个子列表中使用相同的0
和1
对象。小整数也涉及一些缓存,但这并不重要。在一般情况下,子列表没有共同的所有元素。
深层复制是一种避免共享相同对象的副本。
例如,执行后:
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
,tuple
的不可变物体等
所以所有子列表共享相同的0
和1
对象)
请注意,这些图表也可以帮助您了解引用计数的工作原理。 每根绳索都是一个参考,直到一个物体有一个连接到浮标的参考链(即一个标识符),它才能保持活着状态。当没有更多的绳索将物体连接到地面的浮标时,物体就会下沉,并被垃圾收集器摧毁。
答案 3 :(得分:4)
a
是一个列表清单。执行b=a[:]
时,您创建一个新列表,但复制元素。所以b
是一个不同的列表,但元素(子列表)是相同的。
答案 4 :(得分:3)
在这两种情况下,您都可以创建一个独立的列表所以a is b
总是假的。
在第一种情况下,您将其他一些值放入其中一个列表中。
在第二种情况下,您的列表都包含相同的值。
好像你会写
l = []
a = [l, l]
b = [l, l]
a is not b
,然而它们包含相同的数据。
如果您现在修改l
,则可以通过所有a[0]
,a[1]
,b[0]
和b[1]
看到此更改。
答案 5 :(得分:3)
虽然a is b
返回False
,但a[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]]
答案 6 :(得分:0)
当您处理列表中的列表时,可以替代计算上昂贵的深度复制
origvector=[]
for ind in range(0, len(testvector)):
origvector.append(testvector[ind][:])
在这个例子中,&#34; testvector&#34;是一个由n个向量组成的矩阵,每个项目包含一个三项列表。像这样:
{0,1,2}{10,20,30}
{3,4,5}{40,50,60}
{6,7,8}{70,80,90}