python,可变对象,不可变对象,深拷贝,浅拷贝。

学习整理,若有问题,欢迎指正。

python 可变对象,不可变对象

  1. 可变对象

该对象所指定的内存地址上面的值可以被改变,变量被改变后,其所指向的内存地址上面的值,直接被改变,没有发生复制行为,也没有发生开辟新的内存地址行为。

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初始值一样的。

 当改变 复杂子对象中的元素时,浅拷贝值发生了变化; 当改变的值不是复杂子对象,浅拷贝的值没有发生变化。因为 浅拷贝 ,复杂子对象的保存方式是 作为 引用 方式存储的,所以修改 浅拷贝的值 和原来的值都可以 改变 复杂子对象的值。

图解:

 

  • 深度拷贝
    • 即将被复制对象完全再复制一遍作为独立的新个体单独存在。所以改变原有被复制对象不会对已经复制出来的新对象产生影响
    • 上图中已经有说明了。

 

 

 

posted @ 2018-12-05 16:04  blankdog  阅读(1054)  评论(0编辑  收藏  举报