(不)可变类型和深(浅)copy

可变类型和不可变类型

# 可变和不可变数据类型

# 可变类型:值改变,id不变,证明修改的是内存中的原值。
# 不可变类型:值改变,id也改变,证明是产生的新值

# int 是不可变类型
a = 10
print(id(a))  # 4337951280
a = 20
print(id(a))  # 4337951600

# float 是不可变类型

b = 3.14
# print(id(b))  # 4371068592
b = 5.20
# print(id(b))  # 4371068560


# string 是不可变类型

c = 'hello'  
# print(id(c))  # 4298447344
c = 'world'
# print(id(c))  # 4298447408

# 小结: int、float、string都被设计为了一个不可分割的整体,不能够被改变。

# list  是可变类型

x = ['1','2','3']
print(id(x))   # 4337755456
x[0] = 'hello'   # 单独修改列表中的某个值。
print(x)
print(id(x))   # 4337755456

# dict  是可变类型

dic = {'name':'x1ong','age' :'17','addr' : 'China'}
print(id(dict))    # 4307849920
dic['name'] = 'Lxx'  # 单独修改name,key对应的值
print(id(dic))  # 4307849920

深浅copy

引入

定义如下list1列表:

list1 = [
    'x1ong',
    '17',
    ['hello','world']
]

他们在内存中的对应关系:

(不)可变类型和深(浅)copy插图

列表在内存中的对应关系大概是:变量名list1存放在栈区中,而值则存放在堆区。栈区存放了变量名与值的内存地址即0xffff0090。而栈区的变量list1,指向了堆区(值)的内存地址。堆区在记录列表的时候。下标对应每一个列表值的内存地址。

如果我们的代码,在原有的基础上增加变量list2。如下:

list2 = list1   # 将变量list1的值赋值给list2变量
print(list2)

那么,他们再内存中的对应关系则是:

(不)可变类型和深(浅)copy插图1

变量list1和变量list2,指向同一个内存地址,如果list1发生改变,那么list2也会跟着改变。

list1 = [
    'x1ong',
    '17',
    ['hello','world']
]

list1[0] = 'Xxx'
print(list1)  # ['Xxx', '17', ['hello', 'world']]
print(list2) # ['Xxx', '17', ['hello', 'world']]

在内存中的变化则为:

(不)可变类型和深(浅)copy插图2

列表中的下标0与0xffff6600解除绑定,与0xffff5530绑定。

需求

  1. copy一下原列表产生一个新列表
  2. 想让两个列表完全独立,针对的是写独立,而不是读独立。

如何copy列表?

浅copy

如果你想要copy一个列表,并且让新列表与原列表的修改完全隔开(修改原列表不影响新列表)

并且要copy的列表中不存在可变数据类型,那么我们就可以使用浅copy。

list1 = [
    'x1ong',
    '17',
    ['hello','world']
]

# 实验1: 对于不可变类型的重新赋值,都是产生了新值,让原列表的地址索引指向了新的内存地址,并不会印象新列表。
list3 = list1.copy()  # 使用copy()函数将list1变量copy一份赋值给list3变量
list1[0] = 'Xxx'  
list1[1] = '十八' 
list1[2] = '你好世界'
print(list1) # ['XXX', '十八', '123']
print(list3) # ['x1ong', '17', ['hello', 'world']]

在内存中是这样的:

(不)可变类型和深(浅)copy插图3

可以发现,由于将list1[2]的值修改为了"你好世界"是一个字符串,所以它是一个不可变类型。所以会给自己开通一个内存地址。不会覆盖原来的地址。因此list3没有被修改。而list3指向的还是'x1ong''17'以及列表。所以list3的值没有被改变。

list1 = [
    'x1ong',
    '17',
    ['hello','world']
]

# 实验2: 对于可变类型,我们可以改变可变类型中的值,但是内存地址不变。即原列表的索引仍然执行之前的内存地址。于是list3也跟着一起变
list3 = list1.copy()  # 使用copy()函数将list1变量copy一份赋值给list3变量
list1[0] = 'Xxx'  
list1[1] = '十八' 
list1[2][0] = '你好'
list1[2][1] = '世界'
print(list1)  # ['XXX', '十八', ['你好', '世界']]
print(list3)  # ['x1ong', '17', ['你好', '世界']]

在内存是这样的:

(不)可变类型和深(浅)copy插图4

综合以上得知,要想实现copy的新列表与原列表,完全分开,我们就需要有一种可以区分开可变类型和不可变类型的copy机制,这就是深copy。

深copy

如果你想要copy一个列表,并且让新列表与原列表的修改完全隔开(修改原列表不影响新列表),并且要copy的列表中存在可变数据类型,那么我们就可以使用深copy。

import copy

list1 = [
    'x1ong',
    '17',
    ['hello','world']
]

list3 = copy.deepcopy(list1)

#         不可变类型    不可变类型     可变类型
print(id(list1[0]),id(list1[1]),id(list1[2]))
print(id(list3[0]),id(list3[1]),id(list3[2]))

'''
以上print的结果
4309317424 4310332464 4303568448
4309317424 4310332464 4310287040  

可以发现,前两者内存地址是一样的。而最后一个可变类型,他们两者的内存地址是不一致的
'''

list1[0] = 'XXX'
list1[1] = '十八'
list1[2][0] = '你好'
list1[2][1] = '世界' 
print(list1) # ['XXX', '十八', ['你好', '世界']]
print(list3) # ['x1ong', '17', ['hello', 'world']]

在内存中是这样的:

(不)可变类型和深(浅)copy插图5

声明:本文章所整理学习的知识全部由Egon老师传授

Egon老师知乎首页:https://www.zhihu.com/people/xiaoyuanqujing

本文作者: x1ong
免责声明:本博客所有文章仅用于学习交流
转载声明:文章为作者原创文章 转载请注明来源
本文链接: https://www.giaoblog.com/python/16514.html
上一篇
下一篇