Python__slots__魔法
1、__slots__魔法
在Python中,每个类都有实例属性。默认情况下Python⽤⼀个字典(__dict__属性)来保存⼀个对象的实例 属性。这⾮常有⽤,因为它允许我们在运⾏时去设置任意的新属性。
然而,对于有着已知属性的⼩类来说,它可能是个瓶颈。这个字典浪费了很多内存。
Python不能在对象创建时直接分配多个固定量的内存来保存所有的属性。因此如果你创建许多对象(我指的是成千上万个),它会消耗掉很多内存。
不过还是有一个方法来规避这个问题。这个方法需要使用__slots__来告诉Python不要使用字典,而且只给一个固定集合的属性分配空间。
1.1 __slots__
的两大好处
1、节省memory。在上面的例子里,如果我们看看bar
和slotted_bar
就看到,slotted_bar
并没有__dict__
而bar
却含有__dict__
。__slots__
正是通过避免定义动态的数据结构__dict__
来实现对memory的节省(见下面code)
dir(bar) # see __dict__ # >> ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a'] dir(bar_slotted) # no __dict__, instead see __slots__ # >> ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'a'] ## __slots__ save memory: size of bar_slotted is significantly smaller than bar from pympler import asizeof asizeof.asizeof(bar) # >> 256 bytes asizeof.asizeof(bar_slotted) # >> 80 bytes
- access attributes更快。事实上,在CPython的实现里,
__slots__
是一个静态数据结构(static),里面存的是value references,比__dict__
更快(见下面code)。
## (In Ipython) access attribute in bar_slotted is much faster than bar %timeit bar.a = "aaa" # >> 76.8 ns ± 8.34 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) %timeit bar_slotted.a = "aaa" # >> 55.7 ns ± 1.16 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
此处要注意一点,正因为__slots__
是static的,定义了__slots__
之后,你将不能创造新的attribute(见下面例子):
## cannot create an attribute named b for slotted_bar, but OK for bar bar.b = "bbb" # OK bar_slotted.b = "bbb" # Error below # --------------------------------------------------------------------------- # AttributeError Traceback (most recent call last) # <ipython-input-12-9a36b223fad1> in <module> # ----> 1 bar_slotted.b = "bbb" #AttributeError: 'BarSlotted' object has no attribute 'b'
如果你想获得__slots__
的好处并且可以创造新的attribute,你可以将__dict__
作为__slots__
中的一个element(见下面code):
class BarSlottedWithDict(object): __slots__ = "__dict__", "a" def __init__(self, a): self.a = a bar_slotted_w_dict = BarSlottedWithDict(1) bar_slotted_w_dict.b = 2 ## bar_slotted_w_dict has both __dict__ and __slots__ dir(bar_slotted_w_dict) # >> ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'a', 'b']
1.2 什么情况下该使用__slots__
?
当你事先知道class的attributes的时候,建议使用slots来节省memory以及获得更快的attribute access。
注意不应当把防止创造__slots__
之外的新属性作为使用__slots__
的原因,可以使用decorators以及getters,setters来实现attributes control。
内存占用情况
普通方法 > _slots_ = "_dict_", "a" > _slots_ = "a"
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本