Python 对象的分类体系

一、对象的三大分类维度

Python 中的对象可以从三个维度来分类,它们互不冲突,一个对象可以同时属于多个类别:

维度 含义 判断标准
是否可变 对象创建后,内容能否被修改 hash(obj) 可哈希 = 不可变;不可哈希 = 可变
是否序列 对象是否有顺序,能否用下标索引访问 支持 obj[0] 即索引操作
是否可迭代 对象能否被 for...in 循环遍历 支持 iter(obj) 返回迭代器

二、常见类型三维度对照表(核心!)

类型 是否可变 是否序列 是否可迭代 归类
列表(list) ✅ 可变 ✅ 序列 ✅ 可迭代 可变序列
字典(dict) ✅ 可变 ❌ 非序列(无序,键值对) ✅ 可迭代 可变非序列
集合(set) ✅ 可变 ❌ 非序列(无序) ✅ 可迭代 可变非序列
字符串(str) ❌ 不可变 ✅ 序列 ✅ 可迭代 不可变序列
元组(tuple) ❌ 不可变 ✅ 序列 ✅ 可迭代 不可变序列
整数(int) ❌ 不可变 ❌ 非序列 ❌ 不可迭代 不可变非序列
浮点数(float) ❌ 不可变 ❌ 非序列 ❌ 不可迭代 不可变非序列
range ❌ 不可变 ✅ 序列 ✅ 可迭代 不可变序列
文件对象 部分可变 ❌ 非序列 ✅ 可迭代 可变非序列

三、图解关系

复制代码
                    所有 Python 对象
                          │
          ┌───────────────┴───────────────┐
          │                               │
      可变对象                         不可变对象
    (内容可修改)                      (内容不可修改)
          │                               │
    ┌─────┴─────┐                  ┌──────┴──────┐
    │           │                  │             │
  序列        非序列             序列          非序列
  (有顺序)   (无顺序)           (有顺序)      (无顺序)
    │           │                  │             │
  列表        集合/字典           字符串/元组/range   int/float/布尔
  (可变序列)  (可变非序列)        (不可变序列)    (不可变非序列)
  ✅可迭代     ✅可迭代            ✅可迭代        ❌不可迭代

四、常见坑点汇总(全是易错点)

坑 1:元组 (1, 2, 3) 和字符串 'abc' 都不可变,但都可迭代

复制代码
t = (1, 2, 3)
t[0] = 10   # ❌ 报错 TypeError(不可变)

for i in t:
    print(i)   # ✅ 可以遍历(可迭代)

坑 2:字符串有下标索引,属于序列,但不可变

复制代码
s = 'hello'
s[0] = 'H'   # ❌ 报错 TypeError(不可变)
s[0]         # ✅ 'h'(序列)
for c in s:  # ✅ 可迭代
    print(c)

坑 3:集合和字典都可变、可迭代,但不是序列(无序,无下标)

复制代码
s = {1, 2, 3}
s.add(4)     # ✅ 可变
s[0]         # ❌ 报错 TypeError(非序列)
for i in s:  # ✅ 可迭代
    print(i)

坑 4:元组的陷阱(单元素元组必须有逗号)

复制代码
x = ('test')   # 这是字符串 str,不是元组!
x = ('test',)  # 这才是元组 tuple

坑 5:函数参数的传值 vs 传引用

传入对象类型 函数内部修改 外部是否改变
不可变对象(int/str/tuple) 创建新对象,原对象不变 ❌ 不变
可变对象(list/dict/set) 修改内部元素(不改赋值) ✅ 改变

不变:

复制代码
def change(lst):
    lst.append('new')   # 修改原对象 → 外部改变
    lst = [1, 2, 3]     # 重新赋值 → 外部不变(但极少这样写)

x = ['test']
change(x)
print(x)  # ['test', 'new'](因为执行了 append,没执行重新赋值)
  • lst 是局部变量,最初指向 x 指向的同一个列表。

  • 执行 lst.append('new') 时,修改的是该列表对象。

  • 执行 lst = [1, 2, 3] 时,lst 断开与原对象的连接,指向新创建的列表。x 仍然指向原来的列表

变:

方法一:直接修改原对象(推荐)

不清除原列表,而是修改原列表的内容:

复制代码
def change(lst):
    lst.clear()        # 清空原列表
    lst.extend([1, 2, 3])  # 填充新内容

x = ['test']
change(x)
print(x)  # [1, 2, 3]

方法二:返回新列表并重新赋值

让函数返回新列表,外部接收返回值:

复制代码
def change(lst):
    return [1, 2, 3]   # 返回新列表

x = ['test']
x = change(x)
print(x)  # [1, 2, 3]

方法四:删除原引用,让函数操作全局变量(不推荐)

使用 global 关键字(但会破坏封装性):

复制代码
def change():
    global x
    x = [1, 2, 3]

x = ['test']
change()
print(x)  # [1, 2, 3]