返回文章列表

文章

Python中的__slots__和dataclasses

目录
  1. 在 Python 中,__slots__ 和 dataclasses 都是用来简化类的定义并优化性能或可读性的两种机制,它们可以单独使用,也可以在 Python 3.10+ 中配合使用。
  2. 一、__slots__
  3. 1. 为什么需要 __slots__
  4. 2. 基本用法
  5. 注意事项
  6. class Sub(Base): slots = () # 禁用 dict,只能使用 a ``` 2. 无法为实例添加 __weakref__ 支持 如果需要弱引用,必须在 __slots__ 中显式加上 '__weakref__'。
  7. 二、dataclasses
  8. 1. 基本用法
  9. 2. 常用参数
  10. 三、dataclass 与 __slots__ 的结合(Python 3.10+)
  11. 这样既享受了 dataclass 带来的便捷,又获得了 __slots__ 的内存优化和属性限制。
  12. 四、何时使用
  13. 📎 参考文章

在 Python 中,__slots__dataclasses 都是用来简化类的定义并优化性能或可读性的两种机制,它们可以单独使用,也可以在 Python 3.10+ 中配合使用。#

一、__slots__#

1. 为什么需要 __slots__#

  • 内存优化:普通的 Python 对象使用 __dict__ 存储实例属性,会为每个实例额外分配一个字典;而定义了 __slots__ 后,Python 会为指定的属性分配固定空间,省去 __dict__,能显著减少内存开销。
  • 限制属性:只允许实例拥有 __slots__ 中列出的属性,防止意外动态添加新属性。

2. 基本用法#

class Point:
    __slots__ = ('x', 'y')  # 只允许 x 和 y 两个属性

    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

p = Point(1, 2)
print(p.x, p.y)  # 1 2

# p.z = 3  # AttributeError: 'Point' object has no attribute 'z'

注意事项#

  1. 继承 如果子类没有定义 __slots__,则它仍然有一个 __dict__,可以动态添加属性;如果想在子类也禁用 __dict__,需要在子类也定义空的 __slots__

class Base: slots = ('a',)

class Sub(Base): slots = () # 禁用 dict,只能使用 a ``` 2. 无法为实例添加 __weakref__ 支持 如果需要弱引用,必须在 __slots__ 中显式加上 '__weakref__'#

二、dataclasses#

从 Python 3.7 开始,引入了 dataclasses 模块,用来简化数据容器类的定义。

1. 基本用法#

from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

p = Point(1, 2)
print(p)        # Point(x=1, y=2)
print(p.x, p.y) # 1 2

  • @dataclass 自动生成:
    • __init__
    • __repr__
    • __eq__
    • (可选)__lt__, __le__, __gt__, __ge__

2. 常用参数#

from dataclasses import dataclass, field

@dataclass(order=True, frozen=True)  # order: 生成比较方法;frozen: 使实例不可变
class Person:
    sort_index: int = field(init=False, repr=False)
    name: str
    age: int = field(default=0)
    tags: list[str] = field(default_factory=list)

    def __post_init__(self):
        # 为排序设定 sort_index
        object.__setattr__(self, 'sort_index', self.age)

  • order=True:生成大小比较方法
  • frozen=True:实例属性变为只读
  • field():可定制默认值、是否参与 __repr__/__eq__/__init__

三、dataclass__slots__ 的结合(Python 3.10+)#

Python 3.10 开始,dataclasses 支持 slots=True,可以自动为数据类生成 __slots__

from dataclasses import dataclass

@dataclass(slots=True)
class Vector:
    x: float
    y: float

v = Vector(1, 2)
print(v)        # Vector(x=1, y=2)
# v.z = 3      # AttributeError: no attribute 'z'

这样既享受了 dataclass 带来的便捷,又获得了 __slots__ 的内存优化和属性限制。#

四、何时使用#

  • 单纯想节省内存,并且不需要动态属性: 直接定义带 __slots__ 的常规类。
  • 想快速定义数据载体,并自动生成常用方法: 使用 @dataclass
  • 两者兼得,且运行环境是 Python 3.10+: 使用 @dataclass(slots=True)

这样,你就可以根据自己的需求,在类定义中灵活选择 __slots__@dataclass,或两者结合。

📎 参考文章#