前言
- Python 中不存在值传递,一切传递的都是对象的引用,也可以认为是传址
- 这里会讲三个概念:对象赋值、浅拷贝、深拷贝
名词解释
- 变量:存储对象的引用
- 对象:会被分配一块内存,存储实际的数据,比如字符串、数字、列表
- 引用:变量指向对象,可以理解为指针
实际的一种应用场景
- 有一个变量 a,存储了一个值
- 此时想用另一个变量 b 暂时存储变量 a 的值,以便后续使用
- 然后继续修改变量 a 的值,但修改的时候并不想同步更改变量 b 的值
a=1
b=a
a=2
对象赋值
- 赋值运算符详解:https://www.cnblogs.com/poloyy/p/15083012.html
- Python 的赋值语句并不是创建一个新对象,只是创建了一个共享原始对象引用的新变量
不可变对象的赋值
a = 1 b = a print(a, b) a += 2 print(a, b) print("a id:", id(a)) print("b id:", id(b)) # 输出结果 1 1 3 1 a id: 4564097808 b id: 4564097776
- 修改变量 a 的值,不会同步修改变量 b 的值
- 因为赋值操作 a += 2 后,变量 a 存储的对象引用已经改变了
- 至于具体的原理,可以看看不可变对象、可变对象的详解 https://www.cnblogs.com/poloyy/p/15073168.html
可变对象的赋值
a = [1, 2, 3] b = a print(a, b) a[1] = 22 print(a, b) print("a id:", id(a)) print("b id:", id(b)) # 输出结果 [1, 2, 3] [1, 2, 3] [1, 22, 3] [1, 22, 3] a id: 4567683136 b id: 4567683136
- 修改 a 变量的值,会同步修改变量 b 的值,这不符合上面的说的实际应用场景
- 因为变量 a、b 指向的对象是可变对象,所以它们保存的对象引用都是一样的
拷贝的诞生
- 那如果要让可变对象也能满足上述实际应用场景,要怎么做呢?
- 当然就是拷贝
- 而拷贝又分为浅拷贝、深拷贝,接下来会具体聊一聊两种拷贝的区别
第一个重点总结
- 对于不可变对象来说,赋值操作其实就可以满足上面说的实际应用场景
- 所以!后面要讲的浅拷贝、深拷贝对于不可变对象来说,和赋值操作是一样的效果!
- 记住!浅拷贝、深拷贝只针对可变对象,即列表、集合、字典!
copy 模块
Python 提供了 copy 模块,包含了浅拷贝、深拷贝函数
from copy import copy, deepcopy # 浅拷贝 copy(x) # 深拷贝 deepcopy(x)
浅拷贝
一句话概括:浅拷贝会创建一个新对象,该新对象存储原始元素的引用
浅拷贝后的值是相同的
- 将列表赋值给变量 old_list
- 通过 copy() 方法对 old_list 变量指向的对象进行浅拷贝,并赋值给新变量 new_list
- 因为是对象进行拷贝,所以 new_list 和 old_list 存储的值是相同的
import copy old_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] new_list = copy.copy(old_list) print("Old list:", old_list) print("New list:", new_list) # 输出结果 Old list: [[1, 2, 3], [4, 5, 6], [7, 8, 9]] New list: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
浅拷贝后的会产生一个新的对象
- 虽然 old_list 和 new_list 存储的值是相同的,但浅拷贝的操作是产生了一个新的对象
- 所以 old_list 和 new_list 指向的对象并不是同一个
import copy old_list = [[1, 2], [3, 4]] new_list = copy.copy(old_list) old_list.append([5, 6]) print("Old list:", old_list, "id is :", id(old_list)) print("New list:", new_list, "id is :", id(new_list)) # 输出结果 Old list: [[1, 2], [3, 4], [5, 6]] id is : 4366240704 New list: [[1, 2], [3, 4]] id is : 4366246720
可以看到内存地址是不同的,所以给 old_list 新增一个元素并不会同步让 new_list 也新增
原理图
- 浅拷贝生成了一个新对象,然后赋值给 new_list
- new_list、old_list 指向的列表对象不是同一个,但值相同
- 重点:对于列表对象中的元素,浅拷贝产生的新对象只存储原始元素的引用(内存地址),所以两个列表对象的元素的引用都指向同一个内存地址