Day 050 — 槽(slots)图解
内存布局对比
graph TB
subgraph "普通类实例 (PointNormal)"
A1["PyObject_HEAD (16 bytes)"]
B1["__dict__ pointer"]
C1["dict object (~232 bytes)"]
D1["'x': 1.0"]
E1["'y': 2.0"]
F1["'z': 3.0"]
A1 --> B1
B1 --> C1
C1 --> D1
C1 --> E1
C1 --> F1
end
subgraph "__slots__ 类实例 (PointSlots)"
A2["PyObject_HEAD (16 bytes)"]
B2["slot[0]: x (8 bytes)"]
C2["slot[1]: y (8 bytes)"]
D2["slot[2]: z (8 bytes)"]
A2 --> B2
A2 --> C2
A2 --> D2
end
graph TB
subgraph "普通类实例 (PointNormal)"
A1["PyObject_HEAD (16 bytes)"]
B1["__dict__ pointer"]
C1["dict object (~232 bytes)"]
D1["'x': 1.0"]
E1["'y': 2.0"]
F1["'z': 3.0"]
A1 --> B1
B1 --> C1
C1 --> D1
C1 --> E1
C1 --> F1
end
subgraph "__slots__ 类实例 (PointSlots)"
A2["PyObject_HEAD (16 bytes)"]
B2["slot[0]: x (8 bytes)"]
C2["slot[1]: y (8 bytes)"]
D2["slot[2]: z (8 bytes)"]
A2 --> B2
A2 --> C2
A2 --> D2
end
内存节省对比
┌─────────────────────────────────────────────┐
│ 100,000 个对象内存对比 │
├─────────────────────────────────────────────┤
│ │
│ 普通类: │
│ ├─ 对象本身: ~64 bytes × 100,000 = 6.4 MB │
│ ├─ __dict__: ~232 bytes × 100,000 = 23.2 MB│
│ └─ 总计: ~29.6 MB │
│ │
│ __slots__ 类: │
│ ├─ 对象本身: ~32 bytes × 100,000 = 3.2 MB │
│ ├─ 无 __dict__ │
│ └─ 总计: ~3.2 MB │
│ │
│ 节省: ~26.4 MB (89.2%) │
│ │
└─────────────────────────────────────────────┘
属性访问性能
┌─────────────────────────────────────────────┐
│ 属性访问性能对比 │
├───────────────────────┬─────────────────────┤
│ 普通类 │ __slots__ 类 │
├───────────────────────┼─────────────────────┤
│ obj.x → dict lookup │ obj.x → slot load │
│ ~100 ns │ ~90 ns │
├───────────────────────┼─────────────────────┤
│ obj.x = val │ obj.x = val │
│ ~120 ns │ ~95 ns │
├───────────────────────┼─────────────────────┤
│ 支持动态属性 │ 不支持动态属性 │
│ 有 __dict__ │ 无 __dict__ │
└───────────────────────┴─────────────────────┘
继承规则
┌─────────────────────────────────────────────┐
│ __slots__ 继承规则 │
├─────────────────────────────────────────────┤
│ │
│ class Base: │
│ __slots__ = ('x', 'y') │
│ │
│ class Child(Base): │
│ __slots__ = ('z',) ✅ 声明新槽位 │
│ # x, y 继承自 Base │
│ # z 是自己的新槽位 │
│ │
│ class BadChild(Base): │
│ # ❌ 忘记声明 __slots__ │
│ # 会恢复 __dict__,失去优化效果 │
│ │
└─────────────────────────────────────────────┘
常见陷阱
flowchart TD
A["使用 __slots__"] --> B{需要弱引用?}
B -->|是| C["添加 '__weakref__'"]
B -->|否| D{需要 pickle?}
D -->|是| E["实现 __getstate__/__setstate__"]
D -->|否| F{子类也需要?}
F -->|是| G["子类声明 __slots__"]
F -->|否| H["⚠️ 会恢复 __dict__"]
flowchart TD
A["使用 __slots__"] --> B{需要弱引用?}
B -->|是| C["添加 '__weakref__'"]
B -->|否| D{需要 pickle?}
D -->|是| E["实现 __getstate__/__setstate__"]
D -->|否| F{子类也需要?}
F -->|是| G["子类声明 __slots__"]
F -->|否| H["⚠️ 会恢复 __dict__"]