Skip to content

使用 slots 构建内存高效的类对象

Memory Efficient Class Objects using Slots

ds p503 1

当我们在 Python 中定义一个类时,可以在运行时给它的对象动态添加新属性。例如,看看下面这个 Python 类。

在这里,在运行时给任何对象添加新属性都是完全合法的,如下所示:

ds p503 2

然而,这并不总是被推荐的做法,原因在于:

  • 如果代码假设所有类的实例总是具有相同的属性,这样做可能会引入 bug。
  • 当对象不断累积新属性时,会让代码调试变得困难。
  • 它还会导致模式冲突等问题。

ds p504 1

我们能限制这种动态性吗?当然可以!定义一个带 slots 的类就能做到。简单来说,它允许我们固定一个类对象所能拥有的实例级属性。带 slots 的类声明如下:

ds p504 2

定义一个 slots 属性,列出某个对象可能拥有的所有属性。

搞定!

现在,如果我们尝试在运行时添加一个新属性,就会抛出错误:

ds p505 1

声明一个带 slots 的类还有很多好处。例如,它可以帮助我们避免常见的代码笔误。假设我们想修改 nameA 的名字。

在这里,我们本应引用属性名(小写 n 的 name),却误写成了 Name(大写 N)。一个普通的、未使用 slots 的类并不会报错,如下所示:

ds p505 2

而一个带 slots 的类则会捕获到这个笔误:

ds p506 1

声明一个带 slots 的类还带来了内存上的优势。如下所示,带 slots 的类所创建的对象比普通类的对象消耗更少的内存:

ds p506 2

你可能会问,这是为什么呢?

通常情况下,Python 会为每个对象创建一个字典,把属性

ds p507 1

映射到对应的值。由于字典是可变的,这正是我们能够动态添加/删除属性的原因。但这会带来内存开销,因为 Python 总是会预留空间,以便日后可能新增的属性。

而对于带 slots 的类,Python 则可以省去这个字典:

ds p507 2

因此,对象会消耗更少的内存。最后提醒一句:如果你不打算给对象动态添加新属性,那么创建一个带 slots 的类会更好。事实上,即使你清楚某个类对象最终会拥有哪些属性,但其中有些属性在对象初始化时还不可用,你仍然可以把它们声明在 slots 类属性中,等到程序中它们可用时再赋值。514

本站内容仅供学习交流,版权归原作者所有。