Python “深度复制”和“浅复制”之间存在差异的原因;原始的;及;“非原始”;物体?

Python “深度复制”和“浅复制”之间存在差异的原因;原始的;及;“非原始”;物体?,python,copy,Python,Copy,我知道Python中浅拷贝和深拷贝的区别,问题不在于何时使用其中一种。然而,我觉得这个小例子很有趣,也不直观 from copy import deepcopy a=0 b=deepcopy(a) c=a a+=1 print(a,b,c) 产出:100 from copy import deepcopy a=[0] b=deepcopy(a) c=a a[0]+=1 print(a,b,c) 输出:[1]0[1] 我想知道选择这个设计的原因,因为在我看来这两段代码是

我知道Python中浅拷贝和深拷贝的区别,问题不在于何时使用其中一种。然而,我觉得这个小例子很有趣,也不直观

from copy import deepcopy

 a=0
 b=deepcopy(a)
 c=a
 a+=1
 print(a,b,c)
产出:100

 from copy import deepcopy
 a=[0]
 b=deepcopy(a)
 c=a
 a[0]+=1
 print(a,b,c)
输出:[1]0[1]

我想知道选择这个设计的原因,因为在我看来这两段代码是相当等价的,但是它们的输出是完全不同的。为了让自己更清楚,我想知道为什么对于“基本”变量,deepcopy是一个深度副本,对于“非基本”(但仍然是基本语言的一部分)变量,浅层副本像列表一样?我个人认为这种行为违反直觉
注意:我使用了Python3,在复制过程中链接对象而不是原语是很常见的


代码段之间的区别在于,在第二个代码段中,c是列表a的副本,而列表是对象,因此它们是链接的。而c是第一个代码段中原语的“副本”,它没有链接。

c=a
既不是浅副本,也不是深副本,无论
a
指的是什么。它甚至比这还要肤浅——它只是复制了一个参考。
c
a
在该赋值后都保留对同一对象的引用

在Python中不可能修改int的值。当您对一个int使用
+=
时,Python会将一个(对一个)新int的引用分配给您从中检索原始int的位置

对于第一种情况,
a+=1
重新分配
a
变量,而
b
c
继续引用赋值前引用的整数

对于第二种情况,
a[0]+=1
重新分配
a
引用的列表的单元格0<代码>b继续引用未更改的副本,
c
继续引用相同的列表
a
引用。由于此列表已更改状态,因此可以通过
c
变量看到更改


顺便说一句,
deepcopy
旨在生成深度副本,即对返回值的(任意深度)修改不会修改参数,反之亦然。由于在Python中不可能修改int的值,因此int算作其自身的(深层)副本,实际上,
deepcopy
实现只返回其参数(如果其参数是int)

>>> x = 1000
>>> copy.deepcopy(x) is x
True

这里的问题是易变性不变性

python中没有原始和非原始的东西,所有东西都是一个类型,有些只是内置的

您需要了解python如何将数据存储在变量中。假设您来自C语言背景,您可以认为所有python变量都是指针。
所有python变量都存储对变量实际值所在位置的引用

内置的
id
函数在某种程度上让我们可以查看变量值实际存储的位置

>>> x = 12345678
>>> id(x)
1886797010128
>>> y = x
>>> id(y)
1886797010128
>>> y += 1
>>> y
12345679
>>> x
12345678
>>> id(y)
1886794729648
变量
x
指向位置
1886797010128
,位置
1886797010128
保存
10
的值
int
在python中是不可变的类型,这意味着存储在位置
1886797010128
中的数据不能更改

当我们分配
y=x
时,
y
现在也指向相同的地址,因为没有必要为相同的值分配更多的内存

当更改
y
时(请记住
int
是一个不可变类型,其值不能更改),将在新位置
1886794729648
创建一个新的int,并且
y
现在指向新地址处的这个新int对象

当您尝试更新保存不可变数据的变量的值时,也会发生同样的情况

>>> id(x)
140707671077744
>>> x = 30
>>> id(x)
140707671078064
更改具有不可变数据的变量的值只会使该变量指向具有更新值的新对象


对于像
list
这样的可变类型,情况并非如此

>>> a = [1, 2, 3]
>>> b = a
>>> id(a), id(b)
(1886794896456, 1886794896456)
>>> b.append(4)
>>> a
[1, 2, 3, 4]
>>> b
[1, 2, 3, 4]
>>>
a
是一个
列表
并且是可变的,使用
append
等方法更改它实际上会改变地址
1886794896456
处的值。由于
b
也指向相同的地址,因此
a
的值也会更新


deepcopy
在不同的内存位置创建一个新对象,其值与其参数相同,即传递给它的对象

我想知道选择这种设计的原因

这仅仅是因为python是如何被设计成面向对象语言的。在java对象中可以看到类似的行为

我个人认为这种行为违反直觉

from copy import deepcopy

 a=0
 b=deepcopy(a)
 c=a
 a+=1
 print(a,b,c)

直觉来自实践。练习一种语言无助于其他语言的工作方式,不同的语言有不同的设计模式和惯例,我认为应该付出一定的努力来学习它们对于我们即将使用的语言是什么。

请分享一个
c=a
不是肤浅的复制品,甚至比这还要肤浅。它只复制引用。int在Python中也是对象。是的,你是对的,但它们保持了一个惯例,即不能链接“原语”,即使它们在技术上也是对象。这不是发生的事情。没有“链接”副本。相反,Python变量保存对对象的引用,
c=a
会导致
c
a
保存对同一对象的引用—它不会复制任何对象。我要说的是,Python的INT不会引用同一对象,以便遵守您在java等其他编程语言中可以找到的约定。a=0 b=a a+=1打印(b):0这不是真的。两个Python变量可以很好地引用相同的int