在 Python 里,我们经常会遇到这样的疑惑:“为啥我改了一个变量,另一个也变了?”、“= 号到底是复制了值,还是复制了地址?”……这些问题,其实都和“值类型”和“引用类型”有关。今天我们就来搞清楚这个让很多人犯迷糊的知识点。
大纲目录
- 什么是值类型和引用类型?
- Python 中的值类型有哪些?
- Python 中的引用类型有哪些?
- 内存图解:变量赋值到底发生了什么?
- 值类型和引用类型的行为对比
- 函数参数传递:值传递还是引用传递?
- 常见的坑和误解
- 怎么避免这些坑?
- 总结归纳
1. 什么是值类型和引用类型?
- 值类型(Value Type):变量保存的是值本身。
- 引用类型(Reference Type):变量保存的是值的地址(引用),多个变量可能引用同一个值。
说白了,就是你到底是直接拿着东西,还是只是拿着指向它的门牌号。
2. Python 中的值类型有哪些?
虽然 Python 万物皆对象,但我们通常把以下几种类型当作“值类型”来看待:
int
(整数)float
(浮点数)bool
(布尔值)str
(字符串)*(注意它是不可变类型)tuple
(元组)*(不可变,也常被当作值类型)
这些类型的一个共同点是:不可变。一旦你对它们进行修改,Python 会创建一个新的对象。
示例:
a = 10
b = a
a = 20
print(b) # 输出 10,b 不受 a 影响
3. Python 中的引用类型有哪些?
通常来说,只要是可变类型,基本就是引用类型:
list
(列表)dict
(字典)set
(集合)- 自定义的对象(class 实例)
示例:
a = [1, 2, 3]
b = a
a.append(4)
print(b) # 输出 [1, 2, 3, 4],b 跟着变了
4. 内存图解:变量赋值到底发生了什么?
来看个直观示意(伪图):
a = [1, 2]
b = a
图示理解:
a ───▶ [1, 2]
b ───┘
修改 a.append(3)
后:
a ───▶ [1, 2, 3]
b ───┘
说明 a
和 b
指向的是同一个列表对象。
5. 值类型和引用类型的行为对比
操作行为 | 值类型(如 int) | 引用类型(如 list) |
---|---|---|
变量赋值 | 复制值 | 复制引用(地址) |
修改变量 | 不影响其他 | 可能影响其他变量 |
作为函数参数传入 | 拷贝一份 | 传递引用 |
6. 函数参数传递:值传递还是引用传递?
Python 里一切都是对象,参数传递本质上是对象引用的传递。
来看两个例子:
值类型:
def foo(x):
x = 100
a = 10
foo(a)
print(a) # 10,a 不受影响
引用类型:
def foo(lst):
lst.append(100)
a = [1, 2]
foo(a)
print(a) # [1, 2, 100],a 被修改了
7. 常见的坑和误解
坑 1:复制引用当作复制值
a = [1, 2, 3]
b = a
b[0] = 999
print(a) # [999, 2, 3],a 也变了
解决方法:使用 copy() 或 deepcopy()
import copy
b = copy.deepcopy(a)
坑 2:默认参数是引用类型
def foo(lst=[]):
lst.append(1)
return lst
print(foo()) # [1]
print(foo()) # [1, 1],坑在这!
改法:
def foo(lst=None):
if lst is None:
lst = []
lst.append(1)
return lst
8. 怎么避免这些坑?
- 对可变对象做修改前,想清楚是否要复制一份
- 默认参数不要用 list/dict 这类引用类型
- 理解
=
是赋值引用,不是复制内容 - 用
copy
或deepcopy
区分浅拷贝和深拷贝
9. 总结归纳
- 值类型 : 不可变、值复制
- 引用类型 : 可变、地址共享