chore: clean repository history

This commit is contained in:
xixu-me committed 2026-06-05 21:13:11 +08:00
commit e1fb2929c0
661 files changed
+167488

No files matched your search

@@ -0,0 +1,227 @@
---
sources: [summaries/05_Object_model__00_Overview.md, summaries/02_Classes_encapsulation.md]
brief: Python __slots__ 用于限制实例属性集合,并可减少对象内存占用。
---
# Python __slots__
`__slots__` 是 Python 类中的一个特殊类属性,用于声明实例允许拥有的属性名称。它可以阻止对象动态添加未声明的属性,并让 Python 使用更紧凑的内部对象表示,从而减少内存占用。
该概念在 [[summaries/02_Classes_encapsulation]] 中作为 Python 封装机制的一部分出现,和 python encapsulation、python properties、python object memory 等主题密切相关。
## 基本含义
普通 Python 对象通常可以在运行时自由添加新属性:
```python
class Stock:
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
s = Stock('GOOG', 100, 490.10)
s.blah = 42 # 通常是允许的
```
这种灵活性是 Python 对象模型的一部分,但也可能带来问题:
- 拼写错误会意外创建新属性;
- 对象结构不够固定;
- 每个实例通常需要维护一个 `__dict__`,带来额外内存开销。
`__slots__` 可以改变这种行为。
## 用法
在类中定义 `__slots__`,列出允许的实例属性名:
```python
class Stock:
__slots__ = ('name', '_shares', 'price')
def __init__(self, name, shares, price):
self.name = name
self._shares = shares
self.price = price
```
这样,`Stock` 实例只能拥有 `name`、`_shares` 和 `price` 这些属性。
如果尝试设置未声明的属性,会抛出 `AttributeError`:
```python
s = Stock('GOOG', 100, 490.10)
s.name # 'GOOG'
s.blah = 42 # AttributeError
```
在 [[summaries/02_Classes_encapsulation]] 中,示例展示了类似行为:
```python
s.price = 385.15
s.prices = 410.2
# AttributeError: 'Stock' object has no attribute 'prices'
```
这里 `prices` 可能只是 `price` 的拼写错误。使用 `__slots__` 后,这类错误会更早暴露。
## 与封装的关系
`__slots__` 有时看起来像一种封装工具,因为它限制了对象可以拥有的属性集合。但需要注意:
- 它不是严格意义上的访问控制;
- 它不会让属性变成真正私有;
- 它主要限制“能添加哪些属性”,而不是“谁能访问这些属性”。
例如,即使 `_shares` 被写入 `__slots__`,外部代码仍然可以访问它:
```python
s._shares
```
这与 Python 的整体封装哲学一致:Python 更多依赖命名约定,而不是强制私有权限。以下划线开头的属性,如 `_shares`,表示内部实现细节,应由程序员自觉避免直接访问。相关内容见 python encapsulation。
## 与 property 的配合
`__slots__` 经常和 `property` 一起出现。例如,公开属性 `shares` 可以通过 property 管理,而真实数据存储在 `_shares` 中:
```python
class Stock:
__slots__ = ('name', '_shares', 'price')
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
@property
def shares(self):
return self._shares
@shares.setter
def shares(self, value):
if not isinstance(value, int):
raise TypeError('Expected int')
self._shares = value
```
这里需要注意:
- `shares` 是一个 property,不是实际存储数据的普通实例属性;
- 实际值保存在 `_shares` 中;
- 因此 `__slots__` 中应包含 `_shares`,而不是一定要包含 `shares`;
- 对 `self.shares = value` 的赋值会触发 setter;
- setter 再把验证后的值写入 `self._shares`。
这种组合体现了 Python 中常见的受管理属性模式,相关内容见 python properties 和 managed attributes。
## 对 __dict__ 的影响
普通 Python 实例通常有一个实例字典 `__dict__`,用于保存动态属性:
```python
s.__dict__
```
使用 `__slots__` 后,如果没有显式把 `__dict__` 加入 slots,实例通常不再拥有普通的 `__dict__`。这意味着属性不再通过实例字典动态存储,而是通过更固定、更紧凑的结构保存。
因此,在使用 `__slots__` 的类上访问:
```python
s.__dict__
```
通常会失败,或者显示该实例没有 `__dict__`。
这正是 `__slots__` 能减少内存占用的原因之一。相关主题见 python object memory。
## 主要用途
`__slots__` 的主要用途包括:
1. **限制属性集合**
防止外部或内部代码随意添加未声明属性。
2. **发现拼写错误**
例如把 `price` 错写成 `prices` 时,普通对象会创建新属性,而 slots 对象会报错。
3. **节省内存**
对大量实例组成的数据结构尤其有用。
4. **略微提升性能**
因为对象内部表示更紧凑,某些属性访问场景可能更高效。
不过,[[summaries/02_Classes_encapsulation]] 明确强调:`__slots__` 最常见的用途是性能和内存优化,而不是普通日常代码中的封装工具。
## 适用场景
适合使用 `__slots__` 的情况:
- 类被用作轻量数据结构;
- 程序会创建大量实例;
- 实例属性集合固定;
- 内存占用是重要问题;
- 希望避免动态添加属性造成的错误。
例如:
```python
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
```
如果程序要创建数百万个 `Point` 对象,`__slots__` 可能带来明显内存收益。
## 不适用场景
不建议使用 `__slots__` 的情况:
- 普通业务类;
- 属性集合未来可能变化;
- 需要动态添加属性;
- 希望对象支持灵活调试或扩展;
- 没有明确的内存或性能压力。
过早使用 `__slots__` 可能让类变得不够灵活,也可能给继承、调试和扩展带来额外复杂性。
## 与私有属性的区别
`__slots__` 和私有属性约定是两个不同概念:
| 机制 | 作用 | 是否强制访问控制 |
|---|---|---|
| `_name` | 表示内部实现细节 | 否 |
| `property` | 管理属性访问、验证或计算 | 部分控制赋值逻辑 |
| `__slots__` | 限制实例属性集合、优化内存 | 否 |
例如:
```python
class Stock:
__slots__ = ('name', '_shares', 'price')
```
这里 `_shares` 的下划线表示“内部使用”,而 `__slots__` 表示实例只能拥有这些属性。二者可以配合使用,但含义不同。
## 关键注意点
- `__slots__` 是类属性,不是实例属性。
- 它通常写成字符串元组,如 `('name', '_shares', 'price')`。
- 声明后,实例不能随意添加未列出的属性。
- 如果未包含 `__dict__`,实例通常没有普通实例字典。
- 它不等于私有属性,也不提供真正访问控制。
- 它最常用于大量小对象的内存优化。
- 日常代码中不应为了“看起来更封装”而滥用。
## 小结
`__slots__` 是 Python 提供的一种限制实例属性和优化对象内存布局的机制。它可以让类的实例只拥有预先声明的属性,并避免普通实例字典带来的额外开销。虽然它能帮助发现属性拼写错误,也能让对象结构更固定,但其核心价值主要在性能和内存优化,而不是强制封装。
在设计类时,应优先考虑清晰的公共接口、合理的命名约定和必要的 `property` 管理;只有在属性集合稳定且实例数量巨大时,才应考虑使用 `__slots__`。
See also: [[summaries/05_Object_model__00_Overview]]