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