__slots__ 与内存优化
使用 __slots__ 减少每个实例的内存开销。
__slots__ 与内存优化 是 CoddyKit 上的免费 Python Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Python Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Python Academy 课程共包含 4 节课。
默认的 __dict__ 开销
默认情况下,每个 Python 实例都会将属性存储在 __dict__ 字典中,每个实例通常会产生 200–400 字节的显著内存开销。
import sys
class Point:
def __init__(self, x, y): self.x, self.y = x, y
p = Point(1, 2)
print(sys.getsizeof(p.__dict__)) # ~200 bytes声明 __slots__
将 __slots__ 定义为属性名称序列。Python 会分配固定大小的数组,而不是字典,从而节省内存。
class SlottedPoint:
__slots__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y
p = SlottedPoint(1, 2)
print(p.x, p.y) # 1 2
# p.z = 3 # AttributeError: no __dict__节省内存
使用 __slots__ 时,与基于字典的实例相比,每个实例通常可节省 30%–50% 的内存——在创建数百万个对象时,这一节省十分可观。
import sys
class Reg:
def __init__(self, x, y): self.x, self.y = x, y
class Slotted:
__slots__ = ("x","y")
def __init__(self, x, y): self.x, self.y = x, y
print(sys.getsizeof(Reg(1,2))) # ~56 bytes + ~200 dict
print(sys.getsizeof(Slotted(1,2))) # ~56 bytes (no dict)默认没有 __dict__
使用 __slots__ 的类没有 __dict__,因此创建实例后无法添加任意属性。
class Config:
__slots__ = ("host", "port")
def __init__(self, h, p): self.host, self.port = h, p
c = Config("localhost", 8080)
# c.debug = True # AttributeError
print(hasattr(c, "__dict__")) # False使用 __slots__ 保留 __dict__
将 "__dict__" 包含在 __slots__ 中,可以在预先声明常用属性为插槽的同时,保留每个实例的字典。
class Hybrid:
__slots__ = ("x", "__dict__")
def __init__(self, x):
self.x = x
h = Hybrid(1)
h.extra = "dynamic" # allowed
print(h.extra)__slots__ 中的 __weakref__
使用 __slots__ 的类会失去弱引用支持。将 "__weakref__" 添加到 __slots__ 中即可重新启用该支持。
import weakref
class Node:
__slots__ = ("value", "__weakref__")
def __init__(self, v): self.value = v
n = Node(42)
ref = weakref.ref(n)
print(ref()) # <Node object>继承与 __slots__
如果父类不使用 __slots__,子类仍会获得一个 __dict__。要实现完整的内存节省,整个继承层次结构都必须定义 __slots__。
class Base:
__slots__ = ("x",)
class Child(Base):
__slots__ = ("y",) # no __dict__
class WithoutSlots(Base):
pass # gets __dict__ from object内存基准测试
使用 tracemalloc 或 pympler,测量添加 __slots__ 前后的实际内存使用量。
import tracemalloc
tracemalloc.start()
points = [SlottedPoint(i, i) for i in range(100_000)]
current, peak = tracemalloc.get_traced_memory()
tracemalloc.stop()
print(f"Peak: {peak/1024/1024:.1f} MB")__slots__ 与对象序列化
使用 __slots__ 的对象可以进行序列化:您可以定义 __getstate__ 和 __setstate__,也可以使用从 Python 3 开始能够自动处理插槽的默认序列化协议。
import pickle
class Point:
__slots__ = ("x", "y")
def __init__(self, x, y): self.x, self.y = x, y
p = Point(3, 4)
data = pickle.dumps(p)
p2 = pickle.loads(data)
print(p2.x, p2.y) # 3 4带有 __slots__ 的数据类——Python 3.10+
将 slots=True 传给 @dataclass,即可自动生成 __slots__。
from dataclasses import dataclass
@dataclass(slots=True)
class Vector:
x: float
y: float
v = Vector(1.0, 2.0)
print(v.x, v.y) # 1.0 2.0
print(hasattr(v, "__dict__")) # False何时使用 __slots__
在以下情况下使用 __slots__:创建数百万个实例、内存受限,或属性访问速度至关重要。如果是通用类且灵活性更重要,则不要使用它。
# Good candidates:
# - Nodes in a large graph or tree
# - Records in a large dataset
# - High-frequency event objects
# Bad candidates:
# - Configuration objects added to ad-hoc
# - Classes that mix-in dynamic attributes快速检查
在使用 __slots__ 的实例上,尝试设置未列于其中的属性时会发生什么?
回顾
__slots__ 用固定大小的数组替代每个实例的 __dict__,可节省 30%–50% 的内存。如果需要字典或弱引用支持,请包含 "__dict__" 或 "__weakref__"。要实现完整的内存节省,整个继承链都必须使用 __slots__。在 Python 3.10+ 中,可使用 @dataclass(slots=True)。
常见问题解答
「__slots__ 与内存优化」课时是免费的吗?
是的 — 「__slots__ 与内存优化」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Python Academy 课程的其余内容,请升级到 CoddyKit PRO。 Python Academy 课程共包含 4 节课。
「__slots__ 与内存优化」这节课中我会学到什么?
使用 __slots__ 减少每个实例的内存开销。 你通过在浏览器中直接运行的动手代码来练习 Python Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Python Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Python Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「__slots__ 与内存优化」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Python Academy 课中编写并运行代码吗?
能。每节 Python Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- Python 类的创建方式
- 编写自定义元类
- 描述器:__get__、__set__、__delete__
- __slots__ 与内存优化