深入浅出讲解Python中的可变类型与不可变类型,以及赋值,浅拷贝与深拷贝的区别


在Python中,数据类型主要分为可变数据类型和不可变数据类型。主要的区别在于可变数据类型的值可以改变,而不可变数据类型的值 一旦创建就不能更改。

在这里插入图片描述
在实际开发过程中,很多时候由于对可变数据集类型与不可变数据类型的疏忽也会导致一些较难排查的bug。

所以这里一个关键的点在于,不可变数据类型在变量创建了之后,这个值就存在了这个地址块中,就不能被改变了,你即使再给同一个变量更新值,那这个值也是重新开辟了一个内存空间存放地址。

不可变数据类型

  • 数值类型:int, float, complex, bool
  • 字符串类型:str
  • 元组:tuple

可变数据类型

  • 列表:list
  • 字典:dict
  • 集合:set

1、不可变数据类型

我们通过输出一部分例子来观察不可变数据类型

# 数字
x = 10
print(id(x))  # 输出x的内存地址
x = 20
print(id(x))  # 输出新的x的内存地址

# 字符串
s = 'hello'
print(id(s))
s = 'world'
print(id(s))

# 元组
t = (1, 2, 3)
print(id(t))
t = (4, 5, 6)
print(id(t))

大家可以执行上面这段代码,会发现每次的值都会是不一样的。这就是我们说的不可变数据类型


这个时候就出现了另一个问题,此时我的变量x获得了一个新的地址,原来的那个地址怎么办呢?
在Python中,每当我们创建一个新的对象(比如一个数字或者字符串),Python就会在内存中为这个对象分配一块新的地址。当我们更改变量x的值时,实际上Python会在内存中为新的值(比如数字20)分配一块新的地址,并将变量x指向这个新的地址。此时,变量x不再指向原来的地址(数字10的地址)。

至于原来的内存地址(存放数字10的那块内存)是否还在,这取决于是否还有其他变量在引用这块内存。如果还有其他变量引用,那么这块内存就会被保留;如果没有其他变量引用,那么Python的垃圾回收机制将会自动回收这块内存,释放内存资源。所以在你的例子中,如果没有其他变量引用数字10,那么数字10所在的内存就会被垃圾回收。这也是python垃圾回收中,我们通常所说的引用-计数原理。

id(x):内存地址A
id(x):内存地址B
没有引用
有引用
创建变量x, x = 10
内存地址A中的值: 10
更新变量x的值, x = 20
内存地址B中的值: 20
检查内存地址A是否还有引用
内存地址A被垃圾回收
内存地址A保持

2、可变数据类型

同样,我们也通过一些例子来观察

id(lst):内存地址A
id(lst):内存地址A
创建变量lst, lst = [1, 2, 3]
内存地址A中的值: [1, 2, 3]
更新变量lst的值, lst.append(4)
内存地址A中的值: [1, 2, 3, 4]
# 列表
lst = [1, 2, 3]
print(id(lst))
lst.append(4)
print(id(lst))

# 字典
d = {
    
    'a': 1, 'b': 2}
print(id(d))
d['c'] = 3
print(id(d))

# 集合
s = {
    
    1, 2, 3}
print(id(s))
s.add(4)
print(id(s))

在上述例子中,当我们改变可变数据类型的值时,其内存地址保持不变,这说明我们在原地修改了对象的值,而不是创建了新的对象。

3、赋值,浅拷贝与深拷贝

在Python中,=,copy()和deepcopy()都是用来复制对象的方法,但它们在处理可变数据类型和不可变数据类型时的行为是不同的。

3.1 赋值操作符 =

=:赋值操作符,它只是创建了一个新的变量,这个变量和原变量指向同一个对象。无论是可变数据类型还是不可变数据类型,都是如此。

# 对于不可变数据类型
x = 10
y = x
y = 20
print(x)  # 输出10

# 对于可变数据类型
lst1 = [1, 2, 3]
lst2 = lst1
lst2.append(4)
print(lst1)  # 输出[1, 2, 3, 4]

3.2 浅拷贝copy()

对于不可变数据类型,由于它们的值不能更改,所以浅拷贝和赋值没有区别;
对于可变数据类型,浅拷贝会创建一个新的对象,但如果对象中还包含可变数据类型的元素,这些元素仍然是指向原对象。这里可以着重看下我下面写的关于可变数据类型两种情况下的例子

# 对于不可变数据类型
import copy
x = 10
y = copy.copy(x)
y = 20
print(x)  # 输出10

# 对于可变数据类型
lst1 = [1, 2, 3]
lst2 = copy.copy(lst1)
lst2.append(4)
print(lst1)  # 输出[1, 2, 3]

lst3 = [1, 2, [3, 4]]
lst4 = copy.copy(lst3)
lst4[2].append(5)
print(lst3)  # 输出[1, 2, [3, 4, 5]]

3.3 深拷贝copy()

深拷贝,无论是可变数据类型还是不可变数据类型,它都会创建一个新的对象,并且会递归地拷贝对象中的元素。

# 对于不可变数据类型
import copy
x = 10
y = copy.deepcopy(x)
y = 20
print(x)  # 输出10

# 对于可变数据类型
lst1 = [1, 2, 3]
lst2 = copy.deepcopy(lst1)
lst2.append(4)
print(lst1)  # 输出[1, 2, 3]

lst3 = [1, 2, [3, 4]]
lst4 = copy.deepcopy(lst3)
lst4[2].append(5)
print(lst3)  # 输出[1, 2, [3, 4]]

因此,=copy()deepcopy()在处理可变数据类型和不可变数据类型时的主要区别在于:=只是创建一个新的变量,而不创建新的对象;copy()会创建一个新的对象,但不会递归地拷贝对象中的元素;deepcopy()会创建一个新的对象,并且会递归地拷贝对象中的元素。

小结

理解Python中的可变与不可变数据类型,对于理解Python中的变量赋值、函数参数传递、深浅拷贝等概念非常重要。

如果需要更多Python源码的,可以参考以下小程序"编程树"中获得大量的python项目源码。

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Demonslzh/article/details/131610709
今日推荐