前言
在python中“一切皆对象”,包括整数(int),小数(float)等
引用
Python解释器维护了一个内部的数据结构,称为命名空间或符号表,它将变量名与对象的内存地址关联起来。当您创建一个变量并将其赋值为某个对象时,实际上是将变量名与对象的内存地址关联起来,从而使变量可以引用该对象。
在python中,变量是一个标识符,解释器遇到这个标识符的时候,会在python的“符号表”里面对应它的位置,并返回它对应的对象的地址。在赋值操作时,符号表将会被编辑,标识符对应的对象地址被改变,使得变量可以引用新的对象。
比如:当我执行:
a = ‘hello’
b = 'world'
拓展:
python的不可变对象用的是引用计数机制。为了节省内存和提高效率,如果创建多个值相同的变量,他们指向的对象一般是相同的。比如
a = 1
b = a
c = b
执行完这段代码之后,a,b,c都指向同一个地址(同个对象),这个地址的引用次数为3。但是当我们改变其中一个变量的值(实际上是想要改变变量引用的对象的值)得时候,其他两个变量的值不会改变。这是为什么?这是因为int是不可变类型,所以实际上当你想要改变变量引用的对象的值的时候,不会真的改变这个对象的值,而是新建了一个对象,然后让变量引用这一个新的对象,相应地“1”的引用次数减少为2。
若执行:b = 2,不会改变0x11的值,而是在内存里新建一个值为2的对象:
(当地址的引用次数变为0,python会回收这块内存。)
浅拷贝
哪些操作属于:除了
copy
模块中的deepcopy
函数,其他均为浅拷贝
就是只拷贝一层的信息,不管是拷贝最外层的引用(直接赋值),还是次外层的引用(切片),只一层。
深拷贝
只有
copy
模块中的deepcopy
函数能够实现
新建新的对象,递归重建完全一样的引用结构。
浅拷贝和深拷贝的区别
浅拷贝和深拷贝的区别不在深浅,而在对引用结构的拷贝是全面的还是部分的。
有时候浅拷贝操作的效果和深拷贝完全相同,比如
a = [1, 2, 3]
b = a[:]
a[0] = 4
print(b[0])
这时b[0]的值没有改变还是1,也就是b = a[:]等价于 b = copy.deepcopy(a)
这是因为切片虽然是浅拷贝,但是不是拷贝a数组这个对象的引用,而是a数组内容里面每个对象的引用,由于前文提到的int类型对象的不可变性,当我们尝试改变a[0]引用的整型对象的值得时候,会新建一个新对象,这个对象的值被改成4,这就和b没什么关系了。
所以我把切片赋值这样的浅拷贝称为“拷贝次外层”的浅拷贝。和我用循环通过下标赋值类似。
但当浅拷贝对象是可变对象时,浅拷贝和深拷贝的区别就显现出来了:
示例:
a = [1, 2, [3, 4]]
b = a[:] # 切片操作
b[0] = 100 # 修改 b 的第一个元素
b[2][0] = 300 # 修改 b 的嵌套列表中的元素
print(a) # 输出:[1, 2, [300, 4]]
在上述示例中,切片复制了包含整数的部分,因此修改 b[0]
不会影响 a
,但它仍然共享嵌套列表中的引用,因此修改 b[2][0]
也会影响 a
。这说明切片是浅拷贝的行为,只适用于最外层的元素。
总结一下,就是如果改变一个变量,另一个也跟着改变了,就是因为两个引用了相同的可变对象。
有什么问题,欢迎评论提出。