Python之一:浅拷贝、深拷贝的区别

当你根据一个现有变量 var 去创建一个新变量 var_new 时,最先想到的大概是 var_new = var了。这个操作也称为赋值

受限于你的使用场景,你会发现这种操作有的时候有用,有的时候总是出现自己意想不到的结果:改变var_new,结果发现var也变了。

示例代码如下:

使用 “=”进行赋值

# 本文代码使用python3.5验证通过

var = [[1,2,3],[4,5,6],[7,8,9]]
var_new = var

var_new[0][0] = 10

print('var id: ', id(var))
print('var_new id: ', id(var_new))

print('var: ', var)
print('var_new: ', var_new)

运行代码,输出结果如下:

var id:  140688176656072
var_new id:  140688176656072
var:  [[10, 2, 3], [4, 5, 6], [7, 8, 9]]
var_new:  [[10, 2, 3], [4, 5, 6], [7, 8, 9]]

分析运行结果,根据打印出的内存地址也可以看出,两个变量指向了相同的内存地址;打印变量结果,可以看到我们对var_new的改变值的操作同样影响到了var

因为赋值操作只是原变量的引用,反映在内存中,并没有开辟新的空间存放该变量的副本。

那么,当我们想根据一个已有变量创建一个独立的副本时,希望对副本的操作不影响原变量,该怎么办呢?

是的,python当然有解决办法。采用python自带的copy模块,并且有以下两种方法:

  1. 浅拷贝(shallow copy)
  2. 深拷贝(deep copy)

copy模块

copy模块用法不难,假如我们对变量var进行拷贝,示例代码如下:

import copy
var_new1 = copy.copy(var)  # 浅拷贝
var_new2 = copy.deepcopy(var)  # 深拷贝

那么,这两种方法的区别和相同点是什么呢?首先我们一一讲解两种方法,最后总结。

1 必备知识准备:

python中的数据类型中,列表、字典、元组、集合是存储大量数据的容器,其中的每个元素可以是像 1,2.3,-23.45 ,'Hello, World’这样的非容器类的数据类型(也称为原子类型),也可以是列表、字典、元组、集合这样的容器数据。

非容器类的数据类型不存在深拷贝、浅拷贝之说。列表、字典、元组、集合等容器类型才会有深、浅拷贝之说。

约定:对于var = [[1,2,3],[4,5,6],[7,8,9]],我们约定[1,2,3]var的某一个一级子对象;而1var的二级子对象,是var[0]的一级子对象。

2 浅拷贝

先来看浅拷贝用法的示例代码:

import copy
var = [[1,2,3],[4,5,6],[7,8,9]]
var_new = copy.copy(var)

var_new[0][0] = 10
var_new[2] = 'a'

print('var id: ', id(var))
print('var_new id: ', id(var_new))

print('var: ', var)
print('var_new: ', var_new)

运行代码,输出结果如下:

var id:  139788348788424
var_new id:  139788348375816
var:  [[10, 2, 3], [4, 5, 6], [7, 8, 9]]
var_new:  [[10, 2, 3], [4, 5, 6], 'a']

分析运行结果,根据打印出的内存地址也可以看出,两个变量指向了不同的内存地址,说明这次创建变量确实在内存中创建了副本;打印变量结果,可以看到我们对var_new的一级子对象的改变不会影响到了var,但是对var_new的二级子对象的改变不会影响到了var

这就是浅拷贝中浅的含义了。浅拷贝只是拷贝第一层子对象。拷贝的内容包括数字、字符串等非容器子对象,以及列表、字典、元组、集合等容器子对象的引用。

3 深拷贝

老样子,先看深拷贝用法的示例代码:

import copy
var = [[1,2,3],[4,5,6],[7,8,9]]
var_new = copy.deepcopy(var)

var_new[0][0] = 10
var_new[2] = 'a'

print('var id: ', id(var))
print('var_new id: ', id(var_new))

print('var: ', var)
print('var_new: ', var_new)

运行代码,输出结果如下:

var id:  139775218323144
var_new id:  139775217910536
var:  [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
var_new:  [[10, 2, 3], [4, 5, 6], 'a']

分析运行结果,根据打印出的内存地址也可以看出,两个变量指向了不同的内存地址,说明这次创建变量确实在内存中创建了副本;打印变量结果,可以看到我们对var_new的一、二级子对象的改变不会影响到了var

够深吧,深拷贝是把一个对象的所有层级的子对象都在内存中开辟空间,创建了副本。

总结

  1. Python中,赋值操作"="只是原对象的引用,不会开辟新内存空间。
  2. Python中,容器类型的数据存在深、浅拷贝之分。
  3. 如果想根据某对象创建一个完全独立的对象,使用copy.deepcopy(),但创建过程较慢。
  4. 如果只想复制对象的第一层元素,使用copy.copy()可以降低物理空间占用,时间较快。但需小心使用,新副本并不完全独立于原对象,需注意改变第2层甚至更深层的值对原对象的影响。

参考

  1. 图解Python深拷贝和浅拷贝

  2. Shallow vs Deep Copying of Python Objects

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值