Python 基础与对象模型
Python 的一切都是对象,理解对象模型是后续所有内容的地基。面试主线三个问题:变量是什么、可变不可变怎么分、拷贝怎么用。
对象三要素
每个对象都有三个属性:id(身份)、type(类型)、value(值)。
a = [1, 2, 3]
b = a # b 和 a 指向同一个对象
a.append(4)
print(b) # [1, 2, 3, 4] b 跟着变变量是名字,不是盒子。赋值只是把名字绑定到对象上,不复制任何东西。这是 Python 和 C/Java 心智模型的核心差异。
== 与 is
| 比较 | 比什么 | 什么时候用 |
|---|---|---|
== |
值相等(调用 __eq__) |
业务比较,绝大多数场景 |
is |
身份相等(id 相同) | 与 None 比较、单例比较 |
a = [1, 2]
b = [1, 2]
a == b # True 值相同
a is b # False 不同对象坑:小整数缓存(-5 到 256)和短字符串可能复用对象,is 恰好为 True,这是实现细节,别依赖。
可变与不可变
| 类别 | 类型 | 特点 |
|---|---|---|
| 不可变 | int、float、str、tuple、frozenset | 值不能改,改了就是新对象(重新绑定) |
| 可变 | list、dict、set | 原地修改,id 不变 |
关键推论:
- 不可变对象可哈希,能当 dict 的 key、set 的元素;list/dict 不行
+=对不可变对象是重新绑定(新对象),对 list 是原地修改。a += b和a = a + b对 list 行为相同但 id 可能不同(__iadd__)- 函数默认参数用可变对象是经典坑(见函数篇)
深浅拷贝
浅拷贝共享内层, 深拷贝全部分离
- 浅拷贝
copy.copy:新对象,但内层可变对象仍共享。改内层会影响两边 - 深拷贝
copy.deepcopy:递归复制所有层,完全独立。代价是慢、可能递归爆栈 - 切片
lst[:]、list(lst)、dict.copy()都是浅拷贝 - 自定义对象可定义
__copy__/__deepcopy__控制行为
面试追问
- 变量赋值是复制吗? 不是。变量是绑定到对象的名字,赋值只绑名字,对象共享
- 为什么 tuple 可以当 dict 的 key? 不可变所以可哈希。含 list 的 tuple 不可哈希(list 可变)
- == 和 is 的区别? == 比值(eq),is 比身份(id)。判 None 用 is
- 浅拷贝和深拷贝的区别? 浅拷贝复制外层共享内层,深拷贝递归全复制。业务上小心共享内层被意外修改
- 函数参数是值传递还是引用传递? 传对象引用。不可变对象改了不生效(重新绑定),可变对象改了生效(原地修改)