skip to content
Logo Lostman_Wang的小站

Python数据类型-可变和不可变对象

一、前置知识

  • 在 Python 中,一切皆为对象
  • Python 中不存在值传递,一切传递的都是对象的引用,也可以认为是传址
  • 变量:存储对象的引用
  • 对象:会被分配一块内存,存储实际的数据,比如字符串、数字、列表
  • 引用:变量指向对象,可以理解为指针

二、定义速记

分类白话描述举例
不可变对象创建后 内容不可改;任何“修改”都会产生 新对象int, float, str, tuple, frozenset
可变对象创建后 内容可原地改;对象 id 保持不变list, dict, set, bytearray, 自定义类实例 …

三、常见内置类型对照表

类型可变?是否可哈希(默认)说明
int任意精度整数
floatIEEE-754 双精度
complex复数
strUnicode 字符串
tuple✅(元素可哈希时)不可变序列
frozenset不可变集合
range惰性整数序列
list可变序列
dict键值映射
set哈希集合
bytearray可变字节数组
自定义类默认 ✅默认 ✅通过 __hash__/__eq__ 控制

四、底层实现差异

1、不可变对象

  • CPython 内部把 小整数 (-5~256)、短 str 做 intern(全局缓存),多次创建指向 同一内存。
  • 任何“修改”都会触发 重新分配内存 与 复制内容,因此旧对象 绝对不可变。

2、可变对象

  • 采用 动态数组(list)、哈希表(dict/set)、可变缓冲区(bytearray)。
  • 原地修改时仅 调整内部结构,对象地址不变,减少了内存分配与拷贝。

五、可哈希性(Hashable)与字典/set 的关系

  • 只有 不可变且实现 __hash__ 与 __eq__ 的对象才可作为 dict 键或 set 元素。
d = {(1, 2): 'tuple_key'} # ✅ tuple 元素可哈希
s = {[1, 2]} # ❌ list 不可哈希 → TypeError

六、函数参数传递行为

  • “对象引用” 传递,但 可变/不可变差异 体现在 能否在原对象上修改:
类型函数内修改对外部影响
int/str/tuple重新绑定新对象不影响外部
list/dict/set原地修改元素影响外部
# 参数传递不可变对象
def test_no_define(age, name):
age = 123
name = "poloyy"
print(age, name)
age = 1
name = "yy"
print(age, name)
test_no_define(age, name)
print(age, name)
# 输出结果
1 yy
123 poloyy
1 yy
# 参数传递可变对象
def test_define(dicts, sets):
dicts['age'] = 24
sets.pop()
print(dicts, sets)
dicts = {"age": 123}
sets = {1, 2}
print(dicts, sets)
test_define(dicts, sets)
print(dicts, sets)
# 输出结果
1 yy
{'age': 123} {1, 2}
{'age': 24} {2}
{'age': 24} {2}

七、性能与并发安全

1、不可变对象

  • 线程安全(无共享写),可放心在多线程环境传递。
  • 大量小对象会触发 内存池 + intern,降低 GC 压力。

2、可变对象

  • 需要 锁 或 不可变副本 保证线程安全。
  • 原地修改避免重复分配,CPU 缓存友好,适合高频写场景。