Python中的引用与拷贝

前言

最近在整理 Python 的笔记,整理到字典的复制的时候,重新学习了下其中的赋值与复制差别

其中容易弄混的地方清晰许多,进行输出下,上篇干货

先上个赋值的示例

1
2
3
4
5
6
7
8
9
a = 1
b = a

print(f'a的值为{a}') # a的值为1
print(f'b的值为{b}') # b的值为1

a = 5
print(f'a的值为{a}') # a的值为5
print(f'b的值为{b}') # b的值为1

可以发现就是一个简单的赋值,清晰明了,不会有任何疑惑是吧

我们尝试换下值的数据类型

1
2
3
4
5
6
a = [0, 1, 2, 3, 4, 5]
b = a

a[1] = 'Hello!'
print(f'a的值为{a}') # a的值为[0, 'Hello!', 2, 3, 4, 5]
print(f'b的值为{b}') # b的值为[0, 'Hello!', 2, 3, 4, 5]

同第一个代码示例的写法,但是为什么其中的 b 竟然随着 a 进行改变了

这就是今天要讲的点,Python 的引用与复制

这个知识点不仅在变量传递的时候使用到,在进行字典操作的时候也很常见,不了解清楚很容易搞混

核心知识点

不讲废话,直入主题,为什么会造成这种差异?
这是因为数值的数据类型特性(是否可变)所导致的

我们知道 Python 有 7 种标准的数据类型

  1. 字符串
  2. 字典
  3. 元组
  4. 数字
  5. 列表
  6. 集合
  7. 布尔

而如果根据是否可变的角度进行分类,可以分为 2 类

  1. 可变数据类型:
    • 列表类型(list)
    • 字典类型(dict)
    • 集合类型(set)
  2. 不可变数据类型:
    • 数字类型(int, float, complex)
    • 字符串类型(str)
    • 元组类型(tuple)

可变与不可变数据是对于引用地址来说

不可变数据类型不允许变量的值发生变化,如果改变,相当于新建一个对象
记住了以上这句话,其实就掌握了全篇核心的知识点🙃

例如你尝试对字符串进行修改,会抛出异常

1
2
3
4
5
6
7
8
9
# 创建一个字符串
my_string = "Hello"

# 尝试修改字符串的第一个字符
my_string[0] = "h"
"""
发生异常: TypeError
'str' object does not support item assignment
"""

引用

字符串

字符串类型属于不可变数据类型

1
2
3
4
5
6
7
8
9
10
a = "1"
b = a

print(f"a的值是:{a},id是:{id(a)}") # a的值是:1,id是:1412627914544
print(f"b的值是:{b},id是:{id(b)}") # b的值是:1,id是:1412627914544


a = "5"
print(f"a的值是:{a},id是:{id(a)}") # a的值是:5,id是:1412630962672
print(f"b的值是:{b},id是:{id(b)}") # b的值是:1,id是:1412627914544

可以发现在第一次 a 赋予 1 的时候,两者的内存地址是相同的
也就是引用的是一个内存空间(使用 id 检测数值一样)

而在第二次对 a 进行赋值的时候

由于字符串属于不可变,不能改变 "1" 这个对象,所以只能新建一个对象 "5"
然后赋值操作,a 的引用对象发生改变,变成了使用对象 "5"

b 的值是在第一次赋值时被复制的,使用的是对象 "1"
由于 "1" 这个对象没有发生改变,所以 b 的值 "1"

也就是说为 a 第二次赋值的时候,相当于重新开辟了个内存空间
可以发现示例中,使用 id 检测数值不一样了

其他数据类型也是类同,下面处字典类型以外仅写示例,就不进行讲解了
只要记住赋予变量的数据类型是否可变即可

数字

数字类型属于不可变数据类型

1
2
3
4
5
6
7
8
9
a = 1
b = a

print(f"a的值是:{a}") # a的值是:1
print(f"b的值是:{b}") # b的值是:1

a = 5
print(f"a的值是:{a}") # a的值是:5
print(f"b的值是:{b}") # b的值是:1

元组

元组类型属于不可变数据类型

1
2
3
4
5
6
7
8
9
a = (1, 1)
b = a

print(f"a的值是:{a}") # a的值是:(1, 1)
print(f"b的值是:{b}") # b的值是:(1, 1)

a = (1, 2)
print(f"a的值是:{a}") # a的值是:(1, 2)
print(f"b的值是:{b}") # b的值是:(1, 1)

列表

列表类型属于可变数据类型

1
2
3
4
5
6
7
8
9
a = [1, 2, 3]
b = a

print(f"a的值是:{a}") # a的值是:[1, 2, 3]
print(f"b的值是:{b}") # b的值是:[1, 2, 3]

a.append(4)
print(f"a的值是:{a}") # a的值是:[1, 2, 3, 4]
print(f"b的值是:{b}") # b的值是:[1, 2, 3, 4]

字典

字典类型属于可变数据类型

1
2
3
4
5
6
7
8
9
a = {"key1": "a", "key2": "b", "key3": "c"}
b = a

print(f"a的值是:{a}") # a的值是:{'key1': 'a', 'key2': 'b', 'key3': 'c'}
print(f"b的值是:{b}") # a的值是:{'key1': 'a', 'key2': 'b', 'key3': 'c'}

a["key4"] = "d"
print(f"a的值是:{a}") # a的值是:{'key1': 'a', 'key2': 'b', 'key3': 'c', 'key4': 'd'}
print(f"b的值是:{b}") # a的值是:{'key1': 'a', 'key2': 'b', 'key3': 'c', 'key4': 'd'}

偷懒

码累了….,,偷会懒😅
其他类似就不写了

总结

  • 不可变数据类型的数据进行多次赋值,是采用新建立对象
  • 可变数据类型的数据进行多次赋值,是采用引用相同内存空间,而非建立新对象

Python中的引用与拷贝
https://linguoguang.com/2024/06/26/Python中的引用与拷贝/
作者
linguoguang
发布于
2024年6月26日
许可协议