0
点赞
收藏
分享

微信扫一扫

『Python学习笔记』Python中的浅拷贝和深拷贝&可变与不可变类型


Python中的浅拷贝和深拷贝&可变与不可变类型

文章目录

  • ​​一. 浅拷贝深拷贝​​
  • ​​1.1. 浅拷贝​​
  • ​​1.2. 深拷贝​​
  • ​​1.3. 总结​​
  • ​​二. Python可变与不可变类型​​

一. 浅拷贝深拷贝

1.1. 浅拷贝

  • 浅拷贝: 是指重新分配一块内存,创建一个新的对象,里面的元素是原对象中子对象的引用
  • 实现方式1: Python 中也提供了相对应的函数 copy.copy(),适用于任何数据类型
  • 实现方式2:对于可变的序列,我们还可以通过切片操作符​​':'​​​完成浅拷贝,如list;对于不可变序列,如元组,使用 tuple()或者切片操作符​​':'​​不会创建一份浅拷贝,相反,它会返回一个指向相同元组的引用
  • 常见的浅拷贝的方法,是使用数据类型本身的构造器,比如下面两个例子:

『Python学习笔记』Python中的浅拷贝和深拷贝&可变与不可变类型_深拷贝

l1 = [1, 2, 3]
l2 = list(l1) # 构造器进行浅拷贝
l2
[1, 2, 3]
l1 == l2
True
l1 is l2
False

s1 = set([1, 2, 3]) # 构造器进行浅拷贝
s2 = set(s1)
s2
{1, 2, 3}
s1 == s2
True
s1 is s2
False

# 我们首先初始化了一个列表 l1,里面的元素是一个列表和一个元组;然后对 l1 执行浅拷贝,赋予 l2。
# 因为浅拷贝里的元素是对原对象元素的引用,因此 l2 中的元素和 l1 指向同一个列表和元组对象。
l1 = [[1, 2], (30, 40)]
l2 = list(l1)

# 接着往下看。l1.append(100),表示对 l1 的列表新增元素 100。这个操作不会对 l2 产生任何影响,
# 因为 l2 和 l1 作为整体是两个不同的对象,并不共享内存地址。操作过后 l2 不变,l1 会发生改变:
l1.append(100)

# 再来看,l1[0].append(3),这里表示对 l1 中的第一个列表新增元素 3。因为 l2 是 l1 的浅拷贝,
# l2 中的第一个元素和 l1 中的第一个元素,共同指向同一个列表,因此 l2 中的第一个列表也会相对应的
# 新增元素 3。操作后 l1 和 l2 都会改变:
l1[0].append(3)

l1
[[1, 2, 3], (30, 40), 100]

l2
[[1, 2, 3], (30, 40)]
# 最后是l1[1] += (50, 60),因为元组是不可变的,这里表示对 l1 中的第二个元组拼接,
# 然后重新创建了一个新元组作为 l1 中的第二个元素,而 l2 中没有引用新元组,因此 l2 并不受影响。
# 操作后 l2 不变,l1 发生改变:
l1[1] += (50, 60)
l1
[[1, 2, 3], (30, 40, 50, 60), 100]

l2
[[1, 2, 3], (30, 40)]

1.2. 深拷贝

  • 深拷贝: 是指重新分配一块内存,创建一个新的对象,并且将原对象中的元素,以递归的方式,通过创建新的子对象拷贝到新对象中。因此,新对象和原对象没有任何关联。
  • 实现方式1: Python 中以 copy.deepcopy() 来实现对象的深度拷贝。比如上述例子写成下面的形式,就是深度拷贝:

import copy
l1 = [[1, 2], (30, 40)]
l2 = copy.deepcopy(l1)
l1.append(100)
l1[0].append(3)

l1
[[1, 2, 3], (30, 40), 100]

l2
[[1, 2], (30, 40)]

1.3. 总结

  • 比较操作符​​'=='​​​表示比较对象间的值是否相等,而​​'is'​​表示比较对象的标识是否相等,即它们 是否指向同一个内存地址
  • 比较操作符​​'is'​​​效率优于​​'=='​​​,因为​​'is'​​​操作符无法被重载,执行​​'is'​​​操作只是简单的获取对象的 ID,并进行比较;而​​'=='​​操作符则会 递归地遍历对象的所有值,并逐一比较
  • 浅拷贝中的元素,是原对象中子对象的引用,因此,如果原对象中的元素是可变的,改变其也会影响拷贝后的对象,存在一定的副作用。
  • 深度拷贝则会递归地拷贝原对象中的每一个子对象,因此拷贝后的对象和原对象互不相关。另外,深度拷贝中会维护一个字典,记录已经拷贝的对象及其 ID,来提高效率并防止无限递归的发生。

二. Python可变与不可变类型

  • 以下所有的内容 都是基于内存地址 来说的。
  • 不可变数据类型: 当该数据类型的对应变量的值发生了改变,那么它对应的内存地址也会发生改变,对于这种数据类型,就称不可变数据类型。
  • 可变数据类型:当该数据类型的对应变量的值发生了改变,那么它对应的内存地址不发生改变,对于这种数据类型,就称可变数据类型。
  • 在python中数据类型有:{整型,字符串,元组}不可变,{集合,列表,字典}可变

# 1. 我们可以发现,当数据发生改变后,变量的内存地址发生了改变,那么整型就是不可变数据类型。
a = 1
print(id(a),type(a))
a = 2
print(id(a),type(a))

1912499232 <class 'int'>
1912499264 <class 'int'>

# 2. 我们可以发现,当数据发生改变后,变量的内存地址发生了改变,那么字符串就是不可变数据类型。
b = 'djx'
print(id(b),type(b))
b = 'djx1996'
print(id(b),type(b))

535056476344 <class 'str'>
535056476624 <class 'str'>

# 3. 元组被称为只读列表,即数据可以被查询,但不能被修改,但是我们可以在元组的元素中存放一个列表,
# 通过更改列表的值来查看元组是属于可变还是不可变。
# 我们可以发现,虽然元组数据发生改变,但是内存地址没有发生了改变,但是我们不可以以此来判定元组就是可变数
# 据类型。我们回头仔细想想元组的定义就是不可变的。我们修改了元组中列表的值,但是因为列表是可变数据类型,
# 所以虽然在列表中更改了值,但是列表的地址没有改变,列表在元组中的地址的值没有改变,所以也就意味着元组没
# 有发生变化。我们就可以认为元组是不可变数据类型,因为元组是不可变的。
c1 = ['1','2']
c = (1,2,c1)
print(c,id(c),type(c))
c1[1] = 'djx'
print(c,id(c),type(c))

result:
(1, 2, ['1', '2']) 386030735432 <class 'tuple'>
(1, 2, ['1', 'djx']) 386030735432 <class 'tuple'>

# 4. 集合我们常用来进行去重和关系运算,集合是无序的。
# 我们可以发现,虽然集合数据发生改变,但是内存地址没有发生了改变,那么集合就是可变数据类型。

s = {1,'d','34','1',1}
print(s,type(s),id(s))
s.add('djx')
print(s,type(s),id(s))

result:
{'d', 1, '34', '1'} <class 'set'> 870405285032
{1, '34', 'djx', '1', 'd'} <class 'set'> 870405285032
# 5. 列表是python中的基础数据类型之一,其他语言中也有类似于列表的数据类型,比如js中叫数组,
# 它是以[]括起来,每个元素以逗号隔开,而且它里面可以存放各种数据类型。
# 我们可以发现,虽然列表数据发生改变,但是内存地址没有发生了改变,那么列表就是可变数据类型。
list = [1,'q','qwer',True]
print(list,type(list),id(list))
list.append('djx')
print(list,type(list),id(list))

result:
[1, 'q', 'qwer', True] <class 'list'> 808140621128
[1, 'q', 'qwer', True, 'djx'] <class 'list'> 808140621128


# 6. 字典是python中唯一的映射类型,采用键值对(key-value)的形式存储数据。python对key进行哈希函数
# 运算,根据计算的结果决定value的存储地址,所以字典是无序存储的。但是在3.6版本后,字典开始是有序
# 的,这是新的版本特征。
# 字典的key值可以是整型,字符串,元组,但是不可以是列表,集合,字典。
# 我们可以发现,虽然字典数据发生改变,但是内存地址没有发生了改变,那么字典就是可变数据类型。
tuple = (1)
dic = {1:2}
d = { tuple:1,'key2':'djx','key3':'li'}
print(d,type(d),id(d))
d['key4'] = 'haha'
print(d,type(d),id(d))

result:
{1: 1, 'key2': 'djx', 'key3': 'li'} <class 'dict'> 256310956320
{1: 1, 'key2': 'djx', 'key3': 'li', 'key4': 'haha'} <class 'dict'> 256310956320


举报

相关推荐

0 条评论