核心要点

  • 能列举:不可变 int/float/str/tuple/frozenset/bytes,可变 list/dict/set/bytearray

  • 区分原地修改(lst.append,id 不变)与重新绑定(x += 1 生成新对象,id 变)

  • 说清传参影响:函数内原地改可变对象会波及调用方,重绑定不会

  • 知道只有不可变且可哈希的对象才能作 dict 键 / set 元素

简要回答

不可变 可变
int, float, str, tuple, frozenset, bytes list, dict, set, bytearray

不可变:「修改」实际是创建新对象并重新绑定

标准回答

不可变 可变
int, float, str, tuple, frozenset, bytes list, dict, set, bytearray

不可变:「修改」实际是创建新对象并重新绑定。如 s += "x" 对 str 生成新串。

可变:可原地改内容而不改身份。如 lst.append(1) 同一 id(lst)

传参影响

python
def f(x, lst):
    x += 1      # 绑定新 int,不影响外部
    lst.append(1)  # 原地改,影响外部

哈希:不可变且实现 __hash__ 的类型可作 dict/set 的键;可变类型不可哈希。

面试陷阱tuple 不可变,但若元素含可变 list,list 内容仍可改。

常见误区

⚠️ 常见踩坑

认为 tuple 内任何情况都完全不可变;修改可变默认参数却以为每次调用独立。

追问

追问 1字符串拼接为什么推荐 join 而非 +=?

str 不可变,循环里 s += x 每次都创建新字符串并复制旧内容,整体是 O(n²)。"".join(parts) 先算总长再一次性分配,是 O(n)。少量几次拼接用 += 无所谓,大循环用 join 或先 append 到 list 再 join。

追问 2如何「复制」可变对象避免副作用?

浅拷贝用 lst.copy() / list(lst) / dict(d),只复制最外层,嵌套的子对象仍共享。要彻底隔离用 copy.deepcopy(),但开销大。判断需求:只在顶层增删用浅拷贝即可,会改到嵌套结构才用深拷贝。

追问 3frozen dataclass 解决什么问题?

@dataclass(frozen=True) 让实例的属性不可重新赋值(赋值抛 FrozenInstanceError),并自动生成 __hash__,于是实例可作 dict 键 / set 元素。适合表示不可变的值对象(配置、坐标)。注意它只冻结绑定,若字段本身是 list 等可变对象,其内容仍可被修改。

延伸学习

与本题相关的知识库文章、术语、工具与行业资讯。