python中深拷贝和浅拷贝_python 中 深拷贝和浅拷贝的理解

在总结 python 对象和引用的时候,想到其实 对于python的深拷贝和浅拷贝也可以很好对其的进行理解。

在python中,对象的赋值的其实就是对象的引用。也就是说,当创建一个对象,然后赋给另外一个变量之后,实际上只是拷贝了这个对象的引用。

我们先用  利用切片操作和工厂方法list方法 来阐述一下浅拷贝。

举个栗子:

Tom = ['Tom', ['age', 10]]

Jack = Tom[:] ……切片操作

June = list(Tom)

接下来查看一下 上述三个变量的引用:

>>> id(Tom)

140466700697824

>>> id(Jack)

140466700700488

>>> id(June)

140466700770192

可以发现,三个变量分别指向了不同的对象。我们再来看一下这三个对象的内容:

>>> Tom

['Tom', ['age', 10]]

>>> Jack

['Tom', ['age', 10]]

>>> June

['Tom', ['age', 10]]

显然这三个对象的内容会是一样的,因为通过上面的 切片操作 以及 工厂函数list 对Tom引用的对象进行了拷贝。接下来再进行进一步验证:

我们对 Jack 和 June 引用的对象进行修改:

>>> Jack[0] = 'Jack'

>>> Jack

['Jack', ['age', 10]]

>>> June[0] = 'June'

>>> June

['June', ['age', 10]]

现在我们打算对Jack的年龄进行修改:

>>> Jack[1][1] = 20

>>> Jack

['Jack', ['age', 20]]

可以看到Jack年龄变为了20; 让我们接下来看一下Tom, June的年龄:

>>> print Tom, Jack, June

['Tom', ['age', 20]] ['Jack', ['age', 20]] ['June', ['age', 20]]

奇怪的事情发生了,我们仅仅是修改了 Jack的年龄, 但是Tom 和 June 的年龄跟着改变了, 这是为什么呢?

这个就涉及到了python中浅拷贝的奥秘:

我们先来看一下上面 Tom, Jack, June中内部元素的 内存地址:

>>> for x in Tom:

... print id(x)

...

140704715293600 --> 'Tom'

140704715147816 --> ['age', 20]

>>> for x in Jack:

... print id(x)

...

140704715286256 --> 'Jack'

140704715147816 --> ['age', 20]

>>> for x in June:

... print id(x)

...

140704715286352 -->'June'

140704715147816 -->['age', 20]

仔细观察可以看到,Tom, Jack, June 三个变量的 岁数元素['age', 20] 指向同一个 对象; 那为什么他们的 名字元素 分别指向不同的对象。这是因为,在python中的分为 可变数据对象(列表,字典) 和 不可变数据对象(整型,字符串,浮点型,元祖)。 正是因为这个原因,他们的 名字元素 为字符串,为不可变数据对象,因此开始为 Jack 和 June 重新命名的时候,实际上内存中已经创建了 Jack 和 June对象。而 岁数元素 是 可变数据对象,所以并不会在内存中创建新的对象,Tom,Jack,June的岁数元素都引用同一个对象,导致修改其中一个会让另外俩个的年龄跟着变化。

这个就是python的浅拷贝,其仅仅是拷贝了 一个整体的对象(应该说一个对象最外面的那一层),而对于对象里面包含的元素不会进行拷贝。

接下来,我们 利用copy中的deepcopy方法来阐述一下 深拷贝:

还是用上面那个栗子:

为了让 Tom, Jack, June之间互不影响,我们用deepcopy方法对Tom进行拷贝生成 Jack 和 June:

>>> Tom = ['Tom', ['age', 10]]

>>> import copy

>>> Jack = copy.deepcopy(Tom)

>>> June = copy.deepcopy(Tom)

>>> Jack

['Tom', ['age', 10]]

>>> June

['Tom', ['age', 10]]

>>> Tom

['Tom', ['age', 10]]

让我们看一下Tom, Jack, June分别指向的内存地址:

>>> print id(Tom), id(June), id(Jack)

140707738759392 140707738799280 140707738797192

三个内存地址不同,然后我们接着改变Jack 和 June的名字,并查看修改后它们的内部元素所指向的内存地址:

>>> Jack[0] = 'Jack'

>>> June[0] = 'June'

>>> Tom

['Tom', ['age', 10]]

>>> Jack

['Jack', ['age', 10]]

>>> June

['June', ['age', 10]]

>>> for x in Tom:

... print id(x)

...

140707738882976 --> 'Tom'

140707738737192 --> ['age', 10]

>>> for x in Jack:

... print id(x)

...

140707738875584 --> 'Jack'

140707738910016 --> ['age', 10]

>>> for x in June:

... print id(x)

...

140707738876640 -->'June'

140707738910160 --> ['age', 10]

可以清楚的看到,他们的内部元素也指向了不同的对象,说明通过deepcopy方法,对元素进行了彻底的拷贝(包括内部元素)。

最后总结一下思路:

思路一:利用切片操作和工厂方法list方法拷贝就叫浅拷贝,只是拷贝了最外围的对象本身,内部的元素都只是拷贝了一个引用而已。

思路二:利用copy中的deepcopy方法进行拷贝就叫做深拷贝,外围和内部元素都进行了拷贝对象本身,而不是引用。

但是对于数字,字符串和其他原子类型对象等,没有被拷贝的说法,即便是用深拷贝,查看id的话也是一样的,如果对其重新赋值,也只是新创建一个对象,替换掉旧的而已。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值