【Python】详解 __slots__

本文详细介绍了Python中__slots__的原理,包括限制动态修改成员、减少内存消耗和提升属性访问速度的功能。通过实验展示了__slots__如何显著降低内存占用,并通过一个单链表实现的例子说明其在实际应用中的优化效果。同时,文章指出__slots__仅对当前类实例生效,对子类无效,除非子类也定义__slots__。
摘要由CSDN通过智能技术生成

目录

一、原理

1.1 说明

1.2 实验

1.3 小结

二、从另一条路线看待 __slots__ 的影响 (选读)

三、应用

3.1 优化单链表实现


一、原理

1.1 说明

Python 作为一门十分灵活的 动态语言,多处设计为顾及灵活而牺牲效率/性能。例如,Python 作为动态语言,类创建好后仍可 动态创建类成员 (如属性/数据成员)。而在静态语言中只能调用类中已有的属性,难以甚至无法添加新属性。

>>> class Point:
	def __init__(self, x=0, y=0):
		self.x = x
		self.y = y

>>> p = Point()
>>> p.z
Traceback (most recent call last):
  File "<pyshell#16>", line 1, in <module>
    p.z
AttributeError: 'Point' object has no attribute 'z'
>>> p.z = 1
>>> p.z
1

上例构造了一个 Point 类,自带属性 x 和 y。实例化 Point 类对象后,实例 p 虽然本无属性 z,但通过 p.z = 1 的赋值操作却可直接将属性 z 添加至实例 p 中。

这种机制虽然很看起来灵活,但实则存在隐患。因为 用户具有随意添加属性的权限时,可能导致未知的问题,特别是面对复杂系统时。故 有时出于严谨与安全考量,不希望用户能够随意动态修改。这时,__slots__ 方法应运而生,例如:

>>> class Point_new:
	__slots__ = ['x', 'y']        # 限制使用
	def __init__(self, x=0, y=0):
		self.x = x
		self.y = y

>>> q = Point_new()
>>> q.z
Traceback (most recent call last):
  File "<pyshell#27>", line 1, in <module>
    q.z
AttributeError: 'Point_new' object has no attribute 'z'
>>> q.z = 1
Traceback (most recent call last):
  File "<pyshell#28>", line 1, in <module>
    q.z = 1
AttributeError: 'Point_new' object has no attribute 'z'

由上例可知,类定义时在构造函数前经过 __slow__ 声明后,实例只能使用关键字 __slots__ 中定义或声明的属性 (数据成员) x 和 y。而原本就不存在于类 Point_new 的属性 z,实例 q 无法再通过赋值的方式动态创建。换言之,对实例属性而言,类属性是只读的,不可以通过实例对实例属性进行增删改 (而只能通过类来增删改类属性,并随之影响实例属性,详见第二节)。

从而, __slots__ 的一个用途是限制用户随意动态修改成员

此外,__slots__ 的另一个功能是减少内存消耗,提升属性访问速度

在 Python 底层实现中,默认使用一个个的 命名空间字典 (namespace dictionary) __dict__ 来保存类的实例属性,从而 允许在运行时动态创建任意新成员 (如第一个例子所示)。可通过实例调用 __dict__ 属性观察 (承接上例):

# 未使用 __slots__
>>> p.__dict__
{'x': 0, 'y': 0, 'z': 1}  # 实例属性字典

# 已使用 __slots__
>>> q.__dict__
Traceback (most recent call last):
  File "<pyshell#32>", line 1, in <module>
    q.__dict__
AttributeError: 'Point_new' object has no attribute '__dict__'

可见,使用 __slots__ 前,类实例调用 __dict__ 属性可查看实例属性字典;然而,使用  __slots__ 后就不可以了,因为 Traceback 显示类对象没有 __dict__ 属性。

使用 __slots__ 前,Python 无法在创建实例时直接分配⼀个固定量的内存来保存所有的实例属性。因此,对于许多“小”类对象而言,使用 dict 维护实例将额外存储许多数据,占用大量不必要的内存,特别是需要创建成千上万实例等情况下。

使用 __slots__ 后,Python 内部的 __new__ 方法将不再创建一个 dict 来保存实例属性而是以一个固定大小的数组取而代之,从而节省空间

事实上,Python 内置的 dict 本质是一个哈希表 (hasht

  • 9
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值