Python中数据类型的可变性,等号赋值与深浅拷贝的区别

作者:Seaton
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

在Python编程语言中,数据类型的可变性是一个重要的概念。了解数据类型的可变性对于编写高效和安全的代码至关重要。本博文将深入探讨Python中的可变和不可变数据类型,以及它们在实际编程中的应用。

一、不可变数据类型

不可变数据类型指的是一旦创建,其值就不能更改的数据类型。常见的不可变数据类型包括:

  • 整数(Integers): 如 42 或 -5
  • 浮点数(Floats): 如 3.14 或 -0.001
  • 字符串(Strings): 如 ‘hello’ 或 “Python”
  • 元组(Tuples): 如 (1, ‘a’, 3.14)

当我们尝试修改这些类型的对象时,实际上是创建了一个新的对象。

二、可变数据类型

可变数据类型指的是创建后可以修改其值的数据类型。常见的可变数据类型包括:

  • 列表(Lists): 如 [1, 2, 3]
  • 字典(Dictionaries): 如 {‘key’: ‘value’}
  • 集合(Sets): 如 {1, 2, 3}

对这类对象进行操作时,可以直接修改其内容,而不需要创建新的对象。

三、python中数据的等号赋值

在Python中,使用等号(=)赋值时,通常是创建了一个对象的引用,而不是复制对象本身。这意味着,如果你将一个对象赋值给另一个变量,那么这两个变量将会引用内存中的同一个对象。

对于不可变数据类型(如整数、浮点数、字符串、元组等),当你对一个变量赋值时,如果改变了这个变量的值,实际上是创建了一个新的对象,并且让这个变量指向这个新对象。因为不可变对象一旦创建就不能更改,所以只能通过重新赋值来改变变量的内容。

对于可变数据类型(如列表、字典、集合等),当你对一个变量赋值时,如果你改变了这个变量的内容(例如,添加、删除或修改列表中的元素),那么这些修改会直接反映在所有引用这个对象的变量上,因为它们都指向内存中的同一个对象。

如果想要创建一个对象的副本,你可以使用浅拷贝(copy模块的copy()函数)或深拷贝(copy模块的deepcopy()函数)。浅拷贝会创建一个新的容器对象,但是容器内的元素仍然是原对象的引用。深拷贝则会递归地复制对象及其包含的所有对象,创建一个完全独立的副本。

四、示例分析

4.1 等号赋值

等号用来赋值,创建对象的引用,而不是复制对象。

当对不可变数据类型进行操作时,实际上会创建一个新的对象。

a = 5
b = a
a = a + 1
print(a)  # 输出 6
print(b)  # 输出 5

在上面的例子中,a 和 b 分别指向内存中不同的对象。

当对可变数据类型进行操作时,可以直接修改其内容,而不需要创建新的对象。

a = [1, 2, 3]
b = a
a.append(4)
print(a)  # 输出 [1, 2, 3, 4]
print(b)  # 输出 [1, 2, 3, 4]

在上面的例子中,a 和 b 指向内存中同一个对象,因此修改 a 也会影响到 b。

特殊情况

虽然不可变数据类型的值本身不能被改变,但是它们可以作为可变数据类型的元素,从而间接地被修改。

a = [1, 2, 3]
b = a
a[0] = 0
print(a)  # 输出 [0, 2, 3]
print(b)  # 输出 [0, 2, 3]

在上面的例子中,虽然整数 1 是不可变的,但是它作为列表 a 的元素,可以被修改。

4.2 浅拷贝

浅拷贝(copy)复制对象及其包含的元素的引用,不复制嵌套的对象。

import copy

a = [1, 2, [3, 4]]
b = copy.copy(a)
a[0] = 0
a[2][0] = 0
print(a)  # 输出 [0, 2, [0, 4]]
print(b)  # 输出 [1, 2, [0, 4]]

4.3 深拷贝

深拷贝(deepcopy)则递归复制对象,以及它包含的所有层次嵌套的对象和元素。

import copy

a = [1, 2, [3, 4]]
b = copy.deepcopy(a)
a[0] = 0
a[2][0] = 0
print(a)  # 输出 [0, 2, [0, 4]]
print(b)  # 输出 [1, 2, [3, 4]]

在上面的例子中,修改 a 中的列表[3, 4]的第一个元素不会影响到 b,因为ba 的深拷贝。

4.4 等号赋值与深浅拷贝的区别

对于不可变数据类型

import copy

a = 1
print(id(a), a)  # 1422344808752 1
b = a
c = copy.copy(a)
d = copy.deepcopy(a)
a = 5
print(id(a), a)  # 1422344808880 5
print(id(b), b)  # 1422344808752 1
print(id(c), c)  # 1422344808752 1
print(id(d), d)  # 1422344808752 1

可以看到,修改了a之后,a的地址变了,说明对于不可变数据类型,修改其值相当于创建了一个新的对象。

  • 等号赋值:这里b被赋予与a相同的引用,因此它们指向相同的对象,所以id(b)与id(a)相同。
  • 浅拷贝和深拷贝:对于不可变对象而言,浅拷贝和深拷贝并没有实际创建新的对象,它们仍然指向原始对象,因此id©和id(d)与id(a)相同。

因此,对于不可变对象,等号赋值与深浅拷贝并无区别。

而对于可变数据类型

import copy

a = [1, 2, [3, 4]]
print(id(a), a)  # 2331941478976 [1, 2, [3, 4]]
print(id(a[0]), a[0])  # 2331933042992 1
print(id(a[2][0]), a[2][0])  # 2331933043056 3
b = a
c = copy.copy(a)
d = copy.deepcopy(a)
a[0] = 5
a[2][0] = 6
print(id(a), a)  # 2331941478976 [5, 2, [6, 4]]
print(id(b), b)  # 2331941478976 [5, 2, [6, 4]]
print(id(c), c)  # 2331941530176 [1, 2, [6, 4]]
print(id(d), d)  # 2331941530240 [1, 2, [3, 4]]
print(id(a[0]), a[0])  # 2331933043120 5
print(id(b[0]), b[0])  # 2331933043120 5
print(id(c[0]), c[0])  # 2331933042992 1
print(id(d[0]), d[0])  # 2331933042992 1
print(id(a[2][0]), a[2][0])  # 2331933043152 6
print(id(b[2][0]), b[2][0])  # 2331933043152 6
print(id(c[2][0]), c[2][0])  # 2331933043152 6
print(id(d[2][0]), d[2][0])  # 2331933043056 3

可以看到,a的地址没有改变,但值全部改变了。说明修改a的值是在原来地址的基础上修改的,而不是创建新的对象。

  • 等号赋值:b的地址和值完全与a相同,因为等号赋值是创建一个引用,b完全随着a的变化而变化;
  • 浅拷贝:c的地址与a不同,值部分相同。因为浅拷贝是创建一个新的对象,但新对象中的子嵌套对象仍然是a的引用,所以不会改变;
  • 深拷贝:d的地址与a不同,值完全相同。因为深拷贝则创建了一个全新的列表,同时递归地复制了所有子对象,包括嵌套的列表。所以全部都是a原来的值。

五、总结

对于不可变数据类型,等号赋值和深浅拷贝没有任何区别,都是创建一个新的引用。
对于可变数据类型,等号赋值、浅拷贝和深拷贝完全不同,

  • 等号赋值创建一个引用,其值完全随着原变量而变;
  • 浅拷贝创建一个对象,但内部子对象还是原来对象的引用;
  • 深拷贝创建一个对象,且内部子对象也是新的对象。
  • 19
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值