python,可变对象,不可变对象,深拷贝,浅拷贝。
学习整理,若有问题,欢迎指正。
python 可变对象,不可变对象
- 可变对象
该对象所指定的内存地址上面的值可以被改变,变量被改变后,其所指向的内存地址上面的值,直接被改变,没有发生复制行为,也没有发生开辟新的内存地址行为。
python可变对象有,列表,字典,set集合
列如:
a = ['1','2','3'] print(id(a)) 2275736586376 a.append('1') print(a) ['1', '2', '3', '1'] print(id(a)) 2275736586376
我们可以看到,在向 a 列表内追加数据的时候,列表 a 前后的id没有发生变化,所以内存地址内,没有开辟新的空间,而是直接在原指定内存地址上面修改的。
图解:
2. 不可变对象
该对象所指定内存中的值不可以被改变,在改变某个对象的值的时候,由于其内存中的值不可以被改变,所以,会把原来的值复制一份再进行改变,这样就会计算机会开辟
一段新的内存空间来存储新的值,python 不可变对象有 int str float number,tuple,None
例如:
c = 'Hello' print(id(c)) 2237718097848 print(c) Hello c = c + 'World' print(id(c)) 2237720838960 print(c) HelloWorld
我们可以看到,c变量在追加的时候,两个id是不一样的,所以内存地址会发生变化,此变化为,将c变量所指向的内存,复制一份,然后再做更改。
图解:
此时,c的内存是指向 HelloWorld的,若Hello内存,长时间没有变量指定,则垃圾回收机制会自动回收的。
3. 等于赋值,并不会产生独立的对象单独存在,而是给原来的数据块打上一个新的标签,其中一个标签值被改变的时候,另一个标签也随之改变。
alist = [1,2,3,['a','b','c']] b = alist #此处id相同,所以指向同以内存地址 print(id(alist)) 2030874445768 print(id(b)) 2030874445768 #向alist内追加元素 alist.append(5) print(alist) [1, 2, 3, ['a', 'b', 'c'], 5] print(b) [1, 2, 3, ['a', 'b', 'c'], 5] #向alist内的列表追加元素 alist[3].append("d") print(alist) [1, 2, 3, ['a', 'b', 'c', 'd'], 5] print(b) [1, 2, 3, ['a', 'b', 'c', 'd'], 5] print(id(alist)) 2030874445768 print(id(b)) 2030874445768
由上面代码看出,alist所指向 [1,2,3,['a','b','c']] 内存地址,b 也是指定的[1,2,3,['a','b','c']]内存地址(id相同),所以a.append(5)后,alist变化,b也变化
图解:
由上图可以看出,无论alist做什么操作,b的内存地址都是指向alist的,所以alist的任何变化,b都会跟着变化。
4. 浅复制,浅复制分为两种情况
- 第一种情况,浅复制的值是不可变对象(数值,字符串,元组)时,与等值复制的是一样的,对象的id值与浅复制对象的id是一样的。
import copy a = 'abcde' b = a print(id(a)) 2030878234584 print(id(b)) 2030878234584 c = copy.copy(a) print(id(c)) 2030878234584 #当改变a的值 a = a + 'fg' print(id(a)) 2030878515304 #由于字符串为不可变对象,所以id(a)会发生变化,但是b,c不会发生变化,还是指向原有内存 print(id(b)) 2030878234584 print(id(c)) 2030878234584
由上面代码可以看出 b = a 其实b,a是指向同一块内存的(id)相同,c=copy.copy(a)也是和a,b指向同一块内存的(id相同),当a发生变化的时候,a会复制原有值,开辟一块新的内存
出来,此时,c与b还是指向原有内存(观察id)
图解:
- 当浅复制的值是可变对象(列表和元组)时会产生一个“不是那么独立的对象”存在。有两种情况:
- 复制的 对象中无 复杂 子对象,原来值的改变并不会影响浅复制的值,同时浅复制的值改变也并不会影响原来的值。原来值的id值与浅复制原来的值不同。
当浅复制的值是可变对象(列表,字典)时,改变的值不是 复杂子对象 代码如下:
list1 = ['1','2','3'] print(id(list1)) 2030878577608 list2 = list1 print(id(list2)) 2030878577608 list3 = list1.copy() print(id(list2)) 2030878577608 #上面三者id都一致 #然后在list1后面append一个新的元素,查看变化情况 list1.append('4') print(list1) ['1', '2', '3', '4', '4'] print(id(list1)) 2030878577608 print(list2) ['1', '2', '3', '4', '4'] print(id(list2)) 2030878577608 print(list3) ['1', '2', '3'] print(id(list3)) 2030878531400 #list3的值与list1,list2的值,不一样,且id也不一样了
当list2 = list1 的时候,完全指向同一块内存空间,list3 = list1.copy()的时候,list3也指向同一块内存空间,当list1发生变化的时候,list2与list1开辟出一块新的空间用来改变,list3还指向原有空间(或者相反)
图解:
- 当浅复制的值是可变对象(列表,字典)时,改变的值是 复杂子对象 代码如下:
l1 = [1,2,['a','b']] l2 = l1 l3 = copy.copy(l2) l4 = copy.deepcopy(l3) print(id(l1)) 2030878621960 print(id(l2)) 2030878621960 print(id(l3)) 2030878622664 print(id(l4)) 2030878622728 #id可以看出,l1,l2的id没有改变,但是,l3,l4的id发生了变化 l1[2].append('a') print(id(l1)) 2030878621960 print(l1) [1, 2, ['a', 'b', 'a']] print(l3) [1, 2, ['a', 'b', 'a']] print(l4) [1, 2, ['a', 'b']] #l1[2].append('a')是改变l1内的可变列表的值,l1的id没有变化,值有变化,l3的值有变化,但是l4的值没有变化。 l1.append(3) print(id(l1)) 2030878621960 print(l1) [1, 2, ['a', 'b', 'a'], 3] print(l2) [1, 2, ['a', 'b', 'a'], 3] print(l3) [1, 2, ['a', 'b', 'a']] print(l4) [1, 2, ['a', 'b']] #l1.append(3)是改变l1的值,l1id没有变化,l3的值没有被改变(还是和上一步的值一样),l4与l1初始值一样的。
当改变 复杂子对象中的元素时,浅拷贝值发生了变化; 当改变的值不是复杂子对象,浅拷贝的值没有发生变化。因为 浅拷贝 ,复杂子对象的保存方式是 作为 引用 方式存储的,所以修改 浅拷贝的值 和原来的值都可以 改变 复杂子对象的值。
图解:
- 深度拷贝
- 即将被复制对象完全再复制一遍作为独立的新个体单独存在。所以改变原有被复制对象不会对已经复制出来的新对象产生影响
- 上图中已经有说明了。