转:python可变类型与不可变类型
一、核心区别
1. 不可变类型(immutable)
值一旦创建就不能原地修改,如果重新赋值、修改内部数据,会直接开辟一块新内存地址,变量指向新地址。
原内存里的数据不会被改动。
常见不可变类型:
int、float、str、bool、tuple(元组)、frozenset2. 可变类型(mutable)
可以在原内存地址上直接修改内部元素,内存地址不变,只是里面的数据变了。
常见可变类型:
list(列表)、dict(字典)、set(集合)二、详细举例说明
1. 不可变类型示例:字符串 str
python
运行
s = "hello"
# 字符串不能原地修改单个字符
# s[0] = "H" # 报错!TypeError
# 只能重新赋值,指向新地址
s = "Hello"
2. 不可变类型:元组 tuple
元组本身不可变,但如果元组里嵌套了可变对象,可以修改里面可变对象的内容:
python
运行
t = (1, 2, [3, 4])
# t[0] = 100 # 报错,元组元素不能替换
t[2].append(5)
print(t) # (1, 2, [3, 4, 5])
3. 可变类型:列表 list
原地修改,内存地址不变:
python
运行
lst = [1,2,3]
lst.append(4)
lst[0] = 10
# 变量指向的内存地址没变
4. 字典 dict、集合 set
同样是原地修改,属于可变类型。
三、id () 验证内存地址
id(变量):查看变量内存地址不可变类型
python
运行
a = 10
print(id(a))
a = 20
print(id(a)) # 地址变了,开辟新空间
可变类型
python
运行
lst = [1,2]
print(id(lst))
lst.append(3)
print(id(lst)) # 地址不变,原地修改
四、关键使用场景考点
1. 函数传参
- 不可变类型传参:函数内修改不会影响外部变量(值传递效果)
- 可变类型传参:函数内原地修改会影响外部原对象(引用传递)
python
运行
def func(lst):
lst.append(999)
my_list = [1,2]
func(my_list)
print(my_list) # [1,2,999] 外部被修改
2. 字典的 key 要求
字典的 key 必须是不可变类型
list、dict、set 不能当 key;int/str/tuple 可以。
3. 默认参数陷阱(面试高频)
默认参数只在函数定义时初始化一次,尽量不要用可变类型当默认参数:
python
运行
def func(x=[]):
x.append(1)
print(x)
func() # [1]
func() # [1,1] 不是预期的[1]
原因:默认参数列表只创建一次,后续调用一直复用同一个列表对象。
五、分类速记表
表格
| 类型 | 是否可变 | 特点 |
|---|---|---|
| int、float、bool、str、tuple、frozenset | 不可变 | 不能原地修改,修改就换内存地址 |
| list、dict、set | 可变 | 可原地增删改,内存地址不变 |
六、易错点总结
- 元组不可变 ≠ 元组里的元素不能变,只是元素引用不能替换
- 只有不可变类型可以哈希(hashable),能作为字典 key、存入 set
- 可变对象作为函数默认参数会有共享陷阱
七、基础选择题(5 道)
-
以下属于不可变类型的是()A. list B. tuple C. dict D. set答案:B解析:元组不可变,列表、字典、集合都是可变类型。
-
下列哪种类型不能作为字典的 key()A. int B. str C. list D. tuple答案:C解析:字典 key 必须可哈希(不可变),列表可变不可哈希,不能做 key。
-
关于元组
t = (1, [2,3]),下列说法正确的是()A. t [1] = [4,5] 可以执行B. t [1].append (4) 可以执行C. 元组内所有元素都不能修改D. 元组属于可变类型答案:B解析:元组约束的是元素引用不能替换,内部嵌套的列表可以原地修改。 -
执行以下代码,两次 id (a) 结果()
python
运行
a = 10
a = a + 5
A. 相同 B. 不同 C. 不确定
答案:B
解析:int 不可变,运算后生成新对象,内存地址改变。
- 以下代码执行后输出结果是()
python
运行
lst = [1,2,3]
lst2 = lst
lst2.append(4)
print(lst)
A. [1,2,3] B. [1,2,3,4] C. 报错
答案:B
解析:lst 和 lst2 指向同一个列表对象,原地修改互相影响。
八、代码输出题(高频必考 8 道)
题 1:int 不可变传参
python
运行
def test(num):
num = 100
print(num)
a = 10
test(a)
print(a)
输出:
plaintext
100
10
解析:int 不可变,函数内只是新建局部变量,不会修改外部全局 a。
题 2:list 可变传参(高频)
python
运行
def test(lst):
lst.append(100)
arr = [1,2]
test(arr)
print(arr)
输出:
解析:列表是可变对象,函数内原地修改,外部原列表同步变化。
[1, 2, 100]
题 3:函数内重新赋值可变对象(易错坑)
python
运行
def test(lst):
lst = [10,20]
lst.append(30)
arr = [1,2]
test(arr)
print(arr)
输出:
解析:函数内
[1, 2]
lst = [10,20]是新建局部列表,不再指向外部 arr,不会修改原列表。题 4:默认参数经典陷阱(面试必问)
python
运行
def func(x=[]):
x.append(1)
print(x)
func()
func()
func()
输出:
plaintext
[1]
[1, 1]
[1, 1, 1]
解析:默认参数只在函数定义时创建 1 次,后续调用共用同一个列表对象。
✅ 正确写法:
python
运行
def func(x=None):
if x is None:
x = []
x.append(1)
print(x)
题 5:字符串不可变
python
运行
s = "python"
# s[0] = "P"
print(s)
输出:直接抛出 TypeError
解析:字符串不支持原地修改单个字符,只能重新赋值。
题 6:元组嵌套可变对象
python
运行
t = (1, 2, [3, 4])
t[2].append(5)
print(t)
输出:
(1, 2, [3, 4, 5])题 7:浅拷贝赋值对比
python
运行
a = [1,2]
b = a
c = a.copy()
b.append(3)
print(a, c)
输出:
[1, 2, 3] [1, 2]题 8:哈希判断
python
运行
print(hash(123))
print(hash("abc"))
# print(hash([1,2]))
列表调用 hash 会直接报错,可变类型不可哈希。
九、简答题(面试口述题)
-
简述可变类型和不可变类型的区别?答:不可变类型:值不能原地修改,修改会生成新对象,内存地址改变,可哈希,能作为字典 key;可变类型:支持原地增删改,内存地址不变,不可哈希,不能作为字典 key。
-
为什么不推荐用可变类型作为函数默认参数?怎么解决?答:函数定义阶段默认参数只初始化一次,多次调用会复用同一个可变对象,导致数据错乱;解决:默认参数设为 None,函数内部再初始化可变对象。
-
元组是不可变类型,为什么元组里可以放列表并且修改列表内容?答:元组保存的是元素的内存引用地址,不允许修改引用指向;列表本身可变,只是修改列表内部数据,没有改变元组存储的引用地址,所以合法。
十、进阶思考题
python
运行
a = (1, [2])
b = a
a[1].append(3)
print(b)
输出:
解析:a、b 指向同一个元组,内部列表原地修改,两个变量看到的数据都会变化。
(1, [2, 3])
浙公网安备 33010602011771号