基础类型与内存模型

可变 vs 不可变类型

不可变 (Immutable)可变 (Mutable)
int, float, boollist
strdict
tupleset
frozensetbytearray
bytes自定义类(默认)

为什么 str 不可变?

  • 可以安全地作为 dict 的 key(hash 值不变)
  • 线程安全
  • Python 会对短字符串做**字符串驻留(interning)**优化
a = "hello"
b = "hello"
print(a is b)  # True(驻留优化,同一对象)
 
a = [1, 2, 3]
b = a
b.append(4)
print(a)  # [1, 2, 3, 4]  -- 同一引用!

内存管理

引用计数(Reference Counting)

每个对象维护一个引用计数器,当计数归零时立即回收。

import sys
a = []
print(sys.getrefcount(a))  # 2(a 本身 + getrefcount 参数)
b = a
print(sys.getrefcount(a))  # 3
del b
print(sys.getrefcount(a))  # 2

缺点:无法处理循环引用

a = []
b = []
a.append(b)
b.append(a)
del a, b  # 引用计数不为 0,但对象无法访问 → 内存泄漏

分代垃圾回收(Generational GC)

Python 用分代 GC 解决循环引用问题:

  • 三代:第 0 代(新对象)、第 1 代、第 2 代
  • 新对象在第 0 代,存活越久晋升到越高代
  • 高代 GC 触发频率更低(长期存活对象很少成为垃圾)
  • gc 模块可手动控制
import gc
gc.collect()           # 手动触发 GC
gc.disable()           # 关闭 GC(性能敏感场景)
gc.get_threshold()     # (700, 10, 10) 默认触发阈值

深拷贝 vs 浅拷贝

import copy
 
original = [[1, 2], [3, 4]]
 
# 浅拷贝:复制容器,但元素仍是引用
shallow = copy.copy(original)
shallow[0].append(99)
print(original)  # [[1, 2, 99], [3, 4]] ← 被影响了!
 
# 深拷贝:递归复制所有对象
deep = copy.deepcopy(original)
deep[0].append(99)
print(original)  # [[1, 2, 99], [3, 4]] ← 不受影响

浅拷贝的几种方式:

lst = [1, 2, 3]
lst2 = lst[:]          # 切片
lst3 = list(lst)       # list()
lst4 = lst.copy()      # .copy()
import copy
lst5 = copy.copy(lst)  # copy.copy()

赋值、is、== 的区别

  • =:绑定变量名到对象(不是复制)
  • is:判断同一对象(id 相同)
  • ==:判断值相等(调用 __eq__
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b)   # True(值相等)
print(a is b)   # False(不同对象)
 
# 小整数缓存(-5 ~ 256)
x = 256
y = 256
print(x is y)   # True(CPython 缓存优化)
 
x = 257
y = 257
print(x is y)   # False(超出缓存范围)

常见面试题

Q: a = b = []a, b = [], [] 的区别?

a = b = []      # a 和 b 指向同一个列表!
a.append(1)
print(b)        # [1]
 
a, b = [], []   # a 和 b 是两个不同的空列表
a.append(1)
print(b)        # []

Q: 为什么不要用可变对象作默认参数?

# 错误写法:默认参数在函数定义时只创建一次!
def append_to(element, to=[]):
    to.append(element)
    return to
 
print(append_to(1))  # [1]
print(append_to(2))  # [1, 2] ← 不是 [2]!
 
# 正确写法
def append_to(element, to=None):
    if to is None:
        to = []
    to.append(element)
    return to