23.Python可变/不可表类型&深浅拷贝

本文深入探讨Python中的数据类型,包括可哈希与不可哈希、可变与不可变的区别。详细解释了不可变类型如int、float、str和tuple的验证,以及可变类型如list、dict和set的行为。通过实例展示了浅拷贝和深拷贝在修改数据时的不同影响,强调了在拷贝过程中如何保持数据独立性。此外,文章还提供了练习题以加深理解。
摘要由CSDN通过智能技术生成

0.可哈希/不可哈希

可哈希(hashable)     即不可变的数据结构
不可哈希(unhashable) 即可变的数据结构 
哈希作用:是一个将大体量数据转化为很小数据的过程,
在固定的时间与复杂度下查询它,哈希对高效的算法和数据结构很重要。

1.可变/不可变

七个标准数据类型中:
不可变数据类型:整型int、浮点型float、字符串str、元组tuple
可变数据类型: 列表list、 字典dicr、 集合set
基于内存地址来说:
改变数据,id没变,就是可变类型。   (修改原来的值)
改变数据,id改变,就是不可变类型。 (产生新的值)

在这里插入图片描述

1.2不可变类型验证
Python中,定义不可变类型时,会在已经定义的对象中寻找对象是否被声明过,
如果对象已经声明过就直接指向该对象,引用计数加1,不会在申请新的内存。

如果改了变量的值,就相当于新建了一个对象,绑定新的对象。
# int是不可变类型
a = 1  # 创建一个值,绑定一个对象的引用,引用计数为1
b = 1  # 绑定已经存在的对象引用,引用计数为2
print(id(a), id(b))  # 1513717872 1513717872
a = 2  # 创建一个值,绑定新的对象引用,解除第一个值得引用
print(id(a), )  # 1513717904 
# float是不可变类型
x = 3.1
print(id(x))
x = 3.2
print(id(x))
"""
1955853640064
1955853640424
"""
#  str是不可变类型
x = "abc"
print(id(x))
x = 'gggg'
print(id(x))
"""
2601477377528
2601477456536
"""
# tuple 元组是不可变类型
"""
t1 = (1, 2, 3)
t1[0] = 1

TypeError:“tuple”对象不支持项分配
"""
1.3可变类型验证
Python中,定义可变类型时,定义同样内容的变量,会重新申请内存地址进行存储。
如果使用方法操作,变量的值发生改变,内存地址是不会变的。(除非变量重新赋值)
列表修改元素的值,变量指向这索引空间没改,只是让索引重新指向一个新的内存地址。

列表list 字典dict 为可变类型,也都是容器类型.
# list是可变类型
list1 = ['aaa', 'bbb', 'ccc']
print(id(list1))

# 修改值
list1[0] = 'AAA'
print(list1)
print(id(list1))
"""
1325891089672
['AAA', 'bbb', 'ccc']
1325891089672
"""
# dict 是可变类型
dic = {'k1': 111, 'k2': 222}
print(id(dic))

# 修改值
dic['k1'] = 3333333333
print(dic)
print(id(dic))
"""
1793239671288
{'k1': 3333333333, 'k2': 222}
1793239671288
"""
# 集合不好演示
1.4补充
关于字典补充:
其中value可以是任意类型
但是key必须是不可变类型
关于元组的补充:
如果元组的元素(可变类型都是容器类型)是可变类型,那个这个容器类型的内的元素可以被修改.
t1 = ([1], 2, 3)
t1[0][0] = 'a'
print(t1)
"""
(['a'], 2, 3)
"""
1.5使用方法
可变类型使用内置方法会产生一个新的值. (使用新的变量去接收这个值)
不可变类型使用内置方法后会直接修改值.
# 不可变类型
str1 = 'hello'

print(str1.split())  # ['hello']
print(str1)  # hello

str2 = str1.split()  # 使用变量接收改变的值
print(str2)  # ['hello']

# 可变类型
list1 = ['hello', ]

print(list1.clear())  # None
print(list1)  # []

list2 = list1.clear()  # 没有必要的操作,已经直接修改了原来的值
print(list2)  # None

2.深/浅拷贝

2.1复制列表
需求:
1.拷贝一下原列表产生一个新的列表
2.想让两个列表完全独立开,并且针对的是改操作的独立而不是读操作
*意思就是读怎么复制都不会问题的,问题出现再修改上,修改一个列表另一个列表不受影响!* 
2.2直接赋值
直接赋值:当创建一个对象list1,然后把它赋给另一个变量list2 的时候,
Python并没有拷贝这个对象,而只是拷贝了这个对象的引用,
原始列表list1改变,被赋值的list1也会做相同的改变.
list1 = ['a', [1, 2]]

# 将 list1 的内存地址绑定给 list2
list2 = list1

print(list1, id(list1))
print(list2, id(list2))
"""
list1 与 list2 都指向同一个内存地址.
['a', [1, 2]] 1656102548744
['a', [1, 2]] 1656102548744
"""

# 修改 list1 的值
list1[0] = 1
print(list1, id(list1))
print(list2, id(list2))
"""
list1 与 list2 都指向同一个内存地址. list1改变, list2特跟着改变. 
[1, [1, 2]] 1592099763464
[1, [1, 2]] 1592099763464
"""

image-20211217125059977

2.3浅拷贝
浅拷贝:把原列表第一层内存地址不加与区分玩全拷贝一份.
list1 = ['a', [1, 2]]


list2 = list1.copy()
print(list1, id(list1), id(list1[0]), id(list1[1]))
print(list2, id(list2), id(list2[0]), id(list2[1]))
"""
# 内存地址不一样,存的值是同一个地址
['a', [1, 2]] 2638936700168 2638933860168 2638936700232
['a', [1, 2]] 2638936700040 2638933860168 2638936700232
"""

list1[0] = 1
print(list1, id(list1), id(list1[0]), id(list1[1]))
print(list2, id(list2), id(list2[0]), id(list2[1]))
"""
不可变类型的值修改,不会影响另一个列表
[1, [1, 2]] 2638936700168 1602906880 2638936700232
['a', [1, 2]] 2638936700040 2638933860168 2638936700232
"""

image-20211217130922829

list1[1][0] = 'a'
print(list1, id(list1), id(list1[0]), id(list1[1]))
print(list2, id(list2), id(list2[0]), id(list2[1]))
"""
可变类型的值修改,影响另一个列表
[1, ['a', 2]] 2638936700168 1602906880 2638936700232
['a', ['a', 2]] 2638936700040 2638933860168 2638936700232
"""

image-20211217131115400

对于不可变类型的赋值,都是产生了新的值,(lsit1)列表索引重新断开旧值绑定新值.
索引指向的内存地址发送改变.

对于可变类型中包含的值,(list1)列表的索引指向仍然指向原来索引,索引的内存地址没变,
变的是索引指向的值,于是新列表也跟着一起改变.
2.4深拷贝
要想拷贝得到的新列表与原列表的改操作完全独立开.
必须有一种可以区分开可变类型与不可变类型的拷贝机制,这就是深拷贝.
import copy


list1 = ['a', [1, 2]]

list2 = copy.deepcopy(list1)
print(list1, id(list1), id(list1[0]), id(list1[1]), id(list1[1][0]), id(list1[1][1]))
print(list2, id(list2), id(list2[0]), id(list2[1]), id(list2[1][0]), id(list2[1][1]))

"""
               变量id不一样   lis t[1]存不可变类型值值    list [1] 可变类型      存的值         存的值
['a', [1, 2]] 1977295991944     1977288826696          1977295992072     1602906880     1602906912
['a', [1, 2]] 1977295965768     1977288826696          1977295964808     1602906880     1602906912
"""

image-20211217134045782

不可变类型不修改值的情况下还是一个地址,可变类型不区分索引.
list1[0] = 1

print(list1)
print(list2)
"""
修改不可变类型,互不影响
[1, [1, 2]]
['a', [1, 2]]
"""

list1[1][0] = 'a'

print(list1)
print(list2)
"""
修改可变类型,互不影响
[1, ['a', 2]]
['a', [1, 2]]
"""

image-20211217133850076

修改值互不影响.
2.5总结
只针对可变类型数据:

赋值的方式:id值一样,两个变都可以修改值,怎么修改它们的结果都是一模一模样的.

浅拷贝:id值不一样,存的值id一样,修改不可变类型不影响另一个变量,修改可变类型影响另一个变量.

深拷贝:id值不一样,存的值一样,怎么修改都不影响另一个变量.

3.练习

1.分别画出下面两个列表在内存中是如何存放的
l1=[11,22,[333,444]]
l2=[11,22,[33,{'name':'kid','age':18}]]
2.元组的元素为一个可变类型时,修改这个容器中元素值的图
3.深浅拷贝变量指向数据的图
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值