Python可变与非可变数据类型

Python 可变(Mutable)与不可变(Immutable)数据类型 是 Python 中非常核心的概念,它直接影响代码的行为、内存使用、函数参数传递、哈希表键的选择等。

下面从定义、分类、特点、常见问题到实际使用场景,一次性讲清楚。

1. 核心概念

  • 不可变类型(Immutable)
    创建后内容无法修改(不能就地改变)。
    任何“修改”操作都会产生一个全新的对象。
  • 可变类型(Mutable)
    创建后内容可以就地修改(in-place modification),对象本身 id 不变。

2. Python 常见数据类型的可变/不可变分类

数据类型是否可变例子id 是否改变(修改后)是否可作为 dict 的 key
int不可变5, -3, 0
float不可变3.14, -0.0
bool不可变True, False
str不可变“hello”, ‘python’
tuple不可变(1, 2, 3), (“a”,)是(元素必须都不可变)
frozenset不可变frozenset([1,2,3])
list可变[1, 2, 3], [“a”, “b”]
dict可变{“a”:1, “b”:2}
set可变{1, 2, 3}
bytearray可变bytearray(b”abc”)

3. 关键区别演示(代码对比)

# 不可变类型:修改会产生新对象
a = 100
print(id(a))          # 例如:140714123456789

a = a + 1             # 实际是创建新对象
print(id(a))          # id 变了,例如:140714123457021

s = "hello"
print(id(s))          # id1

s = s + " world"      # 创建新字符串
print(id(s))          # id 不同

t = (1, 2, 3)
print(id(t))

# t[0] = 100          # TypeError: 'tuple' object does not support item assignment
t = (100, 2, 3)       # 重新赋值,id 改变
print(id(t))
# 可变类型:可以就地修改,id 不变
lst = [1, 2, 3]
print(id(lst))        # id1

lst.append(4)         # 就地修改
lst[0] = 100
print(id(lst))        # id 仍然是 id1

d = {"a": 1}
print(id(d))

d["b"] = 2            # 就地修改
print(id(d))          # id 不变

4. 最容易踩的坑(函数参数传递)

Python 是对象引用传递,但可变与不可变表现不同:

def modify(x):
    x += 1          # 对于不可变类型,x 指向新对象
    print("inside:", x, id(x))

num = 5
print("before:", num, id(num))
modify(num)
print("after: ", num, id(num))   # num 不变!

# 输出:
# before: 5 140714123456789
# inside: 6 140714123457021
# after:  5 140714123456789
def modify_list(lst):
    lst.append(999)     # 就地修改,影响外部对象

mylist = [1, 2, 3]
print("before:", mylist)
modify_list(mylist)
print("after: ", mylist)   # 被修改了!

# 输出:
# before: [1, 2, 3]
# after:  [1, 2, 3, 999]

防御性编程建议
当你不希望函数修改外部可变对象时,建议在函数内部创建副本:

def safe_modify(lst):
    lst = lst.copy()      # 或 lst[:] 
    lst.append(999)
    return lst

5. 为什么 tuple 是不可变的,但可以包含可变对象?

t = (1, [2, 3], 4)
# t[0] = 100          # 报错
t[1].append(99)       # 合法!修改的是 list 的内容
print(t)              # (1, [2, 3, 99], 4)

结论
tuple 本身不可变,指的是它引用的对象集合不能改变(不能增删改元素),但如果元素本身是可变对象,则该元素的内容仍然可以修改。

因此
想作为 dict 的 key 或放入 set 时,tuple 元素必须全部是不可变类型

# 合法
d = {(1, 2): "point"}

# 非法
# d = {(1, [2]): "point"}   # TypeError: unhashable type: 'list'

6. 总结对比表(面试/刷题常考)

问题场景推荐做法 / 结论
需要修改内容用 list、dict、set
需要作为 dict 的 key 或 set 元素用 str、int、tuple(元素必须不可变)、frozenset
需要保证数据不被意外修改用 tuple、frozenset、str
函数参数不想被修改传不可变对象,或在函数内 copy 可变对象
追求哈希性能(dict/set key)优先用不可变类型
内存效率(小整数、短字符串)Python 有小整数池和字符串驻留,相同值 id 可能相同

7. 一句话记忆口诀

  • 不可变:int、float、str、tuple、bool、frozenset
    → 值不变 → id 可能变 → 安全做 key
  • 可变:list、dict、set、bytearray
    → 内容可改 → id 不变 → 慎做 key

有任何具体场景(比如函数参数陷阱、tuple 嵌套可变对象、自定义类的可变性、copy 与 deepcopy 的区别等)想深入讨论,都可以继续问我!

文章已创建 4401

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部