Python中的__slots__有什么作用?

__slots__通过限制实例属性并避免创建__dict__来优化内存,适用于属性固定且对象数量庞大的场景,能显著减少内存占用,但会失去动态添加属性的能力,且影响弱引用和继承行为,实际效果需通过sys.getsizeof()和timeit等工具测量评估。

python中的__slots__有什么作用?

Python中的

__slots__

,说白了,它就是一种内存优化的手段,通过限制类实例只能拥有预先定义好的属性,从而避免为每个实例创建传统的

__dict__

(字典)来存储属性。这样做最直接的好处就是能显著减少每个对象占用的内存空间,尤其是在你需要创建大量相同结构的对象时,效果会非常明显。当然,它也带来了一些副作用,比如你不能再随意给对象添加新属性了,这就像是给你的对象上了一把“锁”,只允许它拥有你明确指定的东西。

解决方案

当我们定义一个类时,Python默认会给每个实例一个

__dict__

属性,这个字典用于动态存储实例的属性。这种灵活性固然好,但对于那些属性固定、数量庞大的对象来说,每个实例都带一个字典,内存开销就变得相当可观。

__slots__

的作用就在于此,它告诉Python解释器:“嘿,这个类的实例,它的属性就只有我

__slots__

里列出来的这些,别的东西一概不要,也别给我搞什么

__dict__

了。”

比如,我们有一个表示二维点的类:

class PointWithoutSlots:    def __init__(self, x, y):        self.x = x        self.y = yclass PointWithSlots:    __slots__ = ('x', 'y')    def __init__(self, x, y):        self.x = x        self.y = y

sys.getsizeof()

来比较一下它们单个实例的内存占用,你会发现

PointWithSlots

的实例通常会小得多。这是因为

PointWithoutSlots

的每个实例都额外背负了一个

__dict__

的开销。对于

__slots__

,属性的存储更像是C语言结构体那样,直接在对象内部预留固定大小的空间,而不是通过字典查找。这不仅省内存,在某些情况下,属性的访问速度也会略有提升,因为省去了字典查找的哈希开销。

立即学习“Python免费学习笔记(深入)”;

但要记住,一旦你用了

__slots__

,你就放弃了实例动态添加属性的能力。尝试给一个

PointWithSlots

的实例添加一个

z

属性,比如

p.z = 3

,就会直接报错

AttributeError

。这就是它“锁定”属性的体现。

何时应该考虑在Python类中使用

__slots__

进行内存优化?

我觉得这个问题问得非常好,因为它直接指向了

__slots__

的核心价值所在。在我看来,你最应该考虑使用

__slots__

的场景,就是当你发现你的应用程序因为创建了巨量的、结构相似的、属性固定的对象而导致内存占用过高时。

举个例子,如果你正在开发一个游戏,每个游戏角色、道具、地图块都可能是一个对象,这些对象成千上万,甚至几十万个。每个对象如果都带一个

__dict__

,那内存消耗就相当惊人了。这时候,如果这些对象的属性是预先确定的(比如一个角色只有生命值、攻击力、位置等),那么使用

__slots__

就能立竿见影地减少总内存占用。

再比如,处理大量数据记录的场景,比如一个ORM(对象关系映射)框架,将数据库中的每一行数据映射成一个Python对象。如果你的数据库表有几百万行,而每个数据行对象只包含固定的几个字段,那么

__slots__

就是个非常值得尝试的优化手段。它能让你的程序在处理同样多的数据时,占用更少的内存,从而可能避免内存溢出,或者让程序在配置较低的机器上也能跑起来。

当然,这不是说所有类都应该用

__slots__

。如果你的类实例数量不多,或者你需要实例具备高度的灵活性,能够随时添加新属性,那么强行使用

__slots__

反而会带来不必要的限制和开发上的不便。这就像是,你不需要为了一辆只在城市里开的家用轿车,去给它装上F1赛车的轮胎——虽然性能可能更好,但成本、维护和实际使用场景都不匹配。

使用

__slots__

会带来哪些潜在的副作用和限制?

嗯,天下没有免费的午餐,

__slots__

在带来内存优势的同时,也确实引入了一些限制和“脾气”。这些东西,我觉得作为开发者,是必须要心里有数的。

首先,最明显的就是实例不能再动态添加新属性了。一旦你定义了

__slots__

,除了

__slots__

里列出的那些属性,你就不能再给这个类的实例赋值新的属性了。这对于那些习惯了Python动态特性的开发者来说,可能需要一段时间去适应。有时候,我也会不小心写出

obj.new_attr = value

,然后就被

AttributeError

教做人。

其次,实例将不再拥有

__dict__

属性。这意味着你不能再通过

obj.__dict__

来查看或遍历实例的所有属性了,也不能使用

vars(obj)

这样的内置函数。这在调试或者某些需要反射机制的场景下,可能会造成一些不便。如果你真的需要在保持

__slots__

优化的同时,又允许部分实例拥有动态属性,你可以把

'__dict__'

这个字符串也加到

__slots__

里,但这样一来,内存优化的效果就会大打折扣,因为又把字典请回来了。

再者,默认情况下,实例也不能被弱引用(weak reference)。如果你需要弱引用功能,就必须把

'__weakref__'

也加到

__slots__

里。这又是一个需要注意的小细节,因为它不像

__dict__

那样,你通常不会默认想到它。

继承关系上,

__slots__

也有点意思。如果子类不定义

__slots__

,那么它就会重新拥有

__dict__

,并且其父类的

__slots__

依然生效。但如果子类也定义了

__slots__

,那么子类的

__slots__

会和父类的

__slots__

合并,形成一个总的属性集合。这里面稍微复杂一点的是多重继承,如果多个父类都定义了

__slots__

,并且它们之间有冲突(比如都定义了同一个名字但类型不同的属性),就可能导致一些难以预料的问题。所以,在复杂的继承体系中,使用

__slots__

需要格外小心。

最后,我觉得还有一点,虽然不算是严格意义上的副作用,但值得一提:它可能会稍微增加代码的“僵硬度”。因为它限制了灵活性,你在设计类的时候就需要更深思熟虑,提前规划好所有可能的属性。这对于快速原型开发或者需求经常变动的项目来说,可能不是最好的选择。

在实践中,如何评估

__slots__

对内存和性能的实际影响?

评估

__slots__

的实际影响,我觉得最关键的一点就是:不要凭空猜测,一定要动手测量! 很多时候,我们觉得某个优化会带来巨大收益,但实际测量下来可能微乎其微,甚至因为引入了复杂性而得不偿失。

1. 内存占用评估:

最直观的方法就是使用

sys.getsizeof()

。你可以创建一个不使用

__slots__

的类实例,再创建一个使用

__slots__

的类实例,然后比较它们的尺寸。

import sysclass NoSlots:    def __init__(self, name, age, city):        self.name = name        self.age = age        self.city = cityclass WithSlots:    __slots__ = ('name', 'age', 'city')    def __init__(self, name, age, city):        self.name = name        self.age = age        self.city = cityns_obj = NoSlots("Alice", 30, "New York")ws_obj = WithSlots("Bob", 25, "London")print(f"NoSlots object size: {sys.getsizeof(ns_obj)} bytes")print(f"WithSlots object size: {sys.getsizeof(ws_obj)} bytes")# 注意:sys.getsizeof()只计算对象本身的内存,不包括它引用的其他对象的内存。# 对于字符串等不可变对象,它们可能在内存中只存一份。# 但对于__dict__的开销,它是很准确的。

要更全面地评估,特别是当你创建了大量对象时,可以考虑使用像

pympler

memory_profiler

这样的第三方库。它们能让你分析整个应用程序的内存使用情况,找出内存占用的大头,这样你就能更准确地判断

__slots__

是否真的解决了你的内存问题。

2. 性能(属性访问速度)评估:

使用

timeit

模块是衡量代码执行时间的好工具。你可以编写小段代码,分别测试使用和不使用

__slots__

的类实例的属性读取或写入操作,然后比较它们的执行时间。

import timeitsetup_code = """class NoSlots:    def __init__(self, x, y):        self.x = x        self.y = yclass WithSlots:    __slots__ = ('x', 'y')    def __init__(self, x, y):        self.x = x        self.y = yns_obj = NoSlots(1, 2)ws_obj = WithSlots(1, 2)"""# 测试属性读取time_ns_read = timeit.timeit("ns_obj.x", setup=setup_code, number=1_000_000)time_ws_read = timeit.timeit("ws_obj.x", setup=setup_code, number=1_000_000)print(f"NoSlots attribute read time: {time_ns_read:.4f} seconds")print(f"WithSlots attribute read time: {time_ws_read:.4f} seconds")# 测试属性写入time_ns_write = timeit.timeit("ns_obj.x = 3", setup=setup_code, number=1_000_000)time_ws_write = timeit.timeit("ws_obj.x = 3", setup=setup_code, number=1_000_000)print(f"NoSlots attribute write time: {time_ns_write:.4f} seconds")print(f"WithSlots attribute write time: {time_ws_write:.4f} seconds")

通常情况下,

__slots__

在属性访问上会有轻微的性能提升,但这往往不如内存节省那么显著。而且,这种性能差异在现代Python版本中,随着解释器的优化,可能变得越来越小,甚至在某些特定场景下,

__dict__

的哈希查找因为缓存等机制反而表现不错。所以,不要盲目追求这点微小的性能提升而牺牲代码的灵活性。

3. 综合考量:

最终的决策,我觉得还是要回到你的具体应用场景。如果内存是瓶颈,而且对象数量巨大,

__slots__

无疑是一个强大的工具。但如果内存不是主要问题,或者你更看重开发效率和代码的灵活性,那么保持默认的

__dict__

行为可能更明智。引入

__slots__

会增加一些开发和维护的复杂性,比如前面提到的继承、动态属性限制等。你需要权衡这些复杂性带来的成本,是否值得那些内存和性能上的收益。有时候,“足够好”的代码,比“极致优化”但难以维护的代码,更有价值。

以上就是Python中的__slots__有什么作用?的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1369961.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Python 中的浅拷贝与深拷贝:区别与应用场景
上一篇 2025年12月14日 10:07:06
如何检查一个字符串是否是回文?
下一篇 2025年12月14日 10:07:15

相关推荐

  • Linus Torvalds 批评 Rust 代码格式化工具:称其“完全疯狂”

    近日,linux创始人linus torvalds在linux内核邮件列表中对rust语言的代码格式化工具rustfmt提出了尖锐批评,称其行为“完全疯狂”。 他提到,在Rust代码中,类似use crate::xyz;这样的导入语句,在经过自动格式化后经常被合并为一行,导致原本清晰的结构变得混乱,…

    用户投稿 2026年9月22日
    100
  • mysql如何分析索引使用 mysql创建索引后的执行计划解读

    mysql如何分析索引使用 mysql创建索引后的执行计划解读mysql如何分析索引使用 mysql创建索引后的执行计划解读mysql如何分析索引使用 mysql创建索引后的执行计划解读mysql如何分析索引使用 mysql创建索引后的执行计划解读

    要分析mysql索引使用和执行计划,核心是通过explain命令查看查询路径,并结合handler_read%状态变量评估索引效率。1. 使用explain命令分析执行计划,关注type、key、extra等列,判断是否高效利用索引;2. 通过show global status like &#82…

    2026年9月22日 用户投稿
    000
  • PHP命令怎么获取执行结果_PHP命令执行结果捕获与返回值处理技巧

    使用exec()可捕获命令输出和返回状态,shell_exec()仅获取输出,proc_open()支持精细控制;需用escapeshellarg()等函数确保安全,并优先使用内置函数替代系统命令。 在PHP中执行系统命令并获取其输出结果和返回状态,是很多运维脚本、自动化工具或与外部程序交互场景下的…

    2026年9月22日
    200
  • mysql怎么添加前缀索引 mysql创建前缀索引的长度选择

    mysql怎么添加前缀索引 mysql创建前缀索引的长度选择mysql怎么添加前缀索引 mysql创建前缀索引的长度选择mysql怎么添加前缀索引 mysql创建前缀索引的长度选择mysql怎么添加前缀索引 mysql创建前缀索引的长度选择

    在mysql中,为长字符串列添加前缀索引的核心目的是优化查询性能并节省存储空间。1. 前缀索引通过仅索引列值的前n个字符实现这一目标;2. 前缀长度的选择需在区分度与存储效率之间取得平衡,理想长度应确保高区分度(如90%以上)且不过度冗余;3. 可通过执行select count(distinct …

    2026年9月22日 用户投稿
    000
  • 《植物大战僵尸:重植版》制作人:价格亲民 未使用AI!

    经典塔防游戏《植物大战僵尸》在问世16年后迎来重磅回归。由PopCap Games精心打造的重制作品——《植物大战僵尸:重植版》将于10月23日正式登陆PlayStation、Xbox、Nintendo Switch以及PC平台。 据The Gamer报道,该游戏执行制作人Jake Neri在采访中…

    2026年9月22日
    200
  • GPU显存时序修改(Timing Tuning)的风险与性能收益

    显存时序调校可提升性能但伴随风险。通过优化时序能降低延迟、提高带宽利用率,增强游戏帧率并配合超频发挥更好效果;但激进设置易引发系统崩溃、花屏、蓝屏等问题,长期不稳定运行还可能损伤硬件,导致保修失效。建议仅限进阶用户在充分准备下使用专业工具小幅调整,并进行严格稳定性测试,普通用户应保持默认设置以确保安…

    2026年9月22日
    100
  • VSCode运行多文件C项目 完整VSCode配置C++开发教程

    要解决#%#$#%@%@%$#%$#%#%#$%@_e2fc++805085e25c9761616c00e065bfe8运行多文件c项目的问题,核心是正确配置tasks.json、launch.json和settings.json文件以定义编译、调试和项目路径。首先安装c/c++扩展插件和可选的编译…

    2026年9月22日
    000
  • Java集合框架在实际项目中的最佳实践

    合理选择集合类型并预设容量,使用不可变集合保护数据,避免遍历中修改结构,可提升Java程序性能与安全性。 Java集合框架是开发中使用最频繁的工具之一,合理使用能显著提升代码的可读性、性能和稳定性。在实际项目中,遵循一些最佳实践可以避免常见陷阱,提高程序健壮性。 选择合适的集合类型 不同场景应选用最…

    2026年9月22日
    000
  • qq浏览器如何清理dns缓存_QQ浏览器强制刷新与清除DNS缓存指南

    首先清除QQ浏览器DNS缓存:打开应用→点击「我的」→进入「设置」→选择「清理浏览数据」→勾选「DNS缓存」→点击「立即清理」;随后可通过在地址栏添加「#refresh」实现强制刷新;也可使用无痕模式验证问题是否由缓存引起。 如果您尝试访问某个网站,但页面加载缓慢或显示错误,可能是由于本地DNS缓存…

    2026年9月22日
    100
  • Canva的AI混合工具如何操作?快速设计专业图形与文本的步骤

    Canva的AI混合功能通过Magic Studio将文本、图像生成与智能设计整合,提升创作效率。首先,使用Magic Write生成文案初稿,克服空白页难题;其次,通过Magic Media输入详细描述生成定制化图像,越具体效果越好;再利用Magic Design上传图片或输入文字自动生成多种设计…

    2026年9月22日
    000
  • vivoY系列微信收款语音播报如何设置?快速设置语音的实用方法

    先在微信内开启收款语音提醒,再确保vivo手机系统中微信的通知权限、后台运行和电池优化设置正确,避免静音或勿扰模式干扰,即可解决语音不响问题。 vivo Y系列手机上设置微信收款语音播报,核心在于微信应用内部的设置,同时需要确保手机系统层面的通知权限和后台运行策略没有限制它。简单来说,就是先在微信里…

    2026年9月22日
    000
  • mysql如何输入批量插入 mysql写多条insert代码教程

    mysql如何输入批量插入 mysql写多条insert代码教程mysql如何输入批量插入 mysql写多条insert代码教程mysql如何输入批量插入 mysql写多条insert代码教程mysql如何输入批量插入 mysql写多条insert代码教程

    mysql批量插入数据有四种主要方式。1.单条insert多值插入,语法简单但可能超包限制且全失败风险高;2.多条insert加事务,减少交互次数但占用资源多;3.load data infile性能最好,需处理文件权限及转义;4.编程语言批量功能灵活处理数据但需额外编码。选择依据为:小数据用多值i…

    2026年9月22日 用户投稿
    000
  • 降压超频(Undervolting)在笔记本与显卡上的能效提升

    降压超频是通过降低芯片核心电压来减少功耗与发热并维持性能的技术。现代处理器和显卡因制造差异,厂商通常设置较高默认电压以确保稳定性,而降压则在保证系统稳定的前提下,去除冗余电压,实现更低功耗与温度。其核心原理为:降低电压→减少功耗与发热→降低风扇转速与电池消耗→提升续航、静音性及持续性能表现。在笔记本…

    2026年9月22日
    300
  • VSCode 怎样配置项目的依赖包自动安装 VSCode 项目依赖包自动安装的配置指南​

    VSCode 怎样配置项目的依赖包自动安装 VSCode 项目依赖包自动安装的配置指南​VSCode 怎样配置项目的依赖包自动安装 VSCode 项目依赖包自动安装的配置指南​VSCode 怎样配置项目的依赖包自动安装 VSCode 项目依赖包自动安装的配置指南​VSCode 怎样配置项目的依赖包自动安装 VSCode 项目依赖包自动安装的配置指南​

    vscode没有内置“一键安装所有依赖”功能,因为它作为通用编辑器需保持轻量与灵活性,无法预设所有项目的依赖管理逻辑;要实现类似效果,最有效的方法是通过配置tasks.json和launch.json实现半自动安装:1. 在项目根目录的.vscode文件夹中创建tasks.json文件,定义“che…

    2026年9月22日 用户投稿
    100
  • MAC的“自动操作”(Automator)怎么用_macOS自动操作创建快速工作流程

    使用Automator可创建自动化工作流程,通过选择“工作流程”并添加操作实现任务串联,保存为“快速操作”或“应用程序”便于调用,结合日历设置定时执行,并可嵌入Shell脚本扩展功能,提升Mac操作效率。 如果您希望在日常操作中提升效率,可以通过自动化重复性任务来节省时间。MAC的“自动操作”(Au…

    2026年9月22日
    000
  • windows怎么开启或关闭休眠模式_休眠模式启用与禁用设置

    首先通过控制面板或命令提示符启用或禁用休眠功能,其次可设置自动休眠时间以节能;操作路径包括图形界面调整与管理员命令执行,适用于Windows 11系统环境。 如果您发现Windows系统的休眠功能未启用或希望禁用该功能以释放磁盘空间,可以通过系统电源设置或命令行工具进行配置。休眠模式会将当前系统状态…

    2026年9月22日
    100
  • 如何在MiniToolMovieMaker中编辑AI视频?免费AI视频剪辑的教程

    如何在MiniToolMovieMaker中编辑AI视频?免费AI视频剪辑的教程如何在MiniToolMovieMaker中编辑AI视频?免费AI视频剪辑的教程如何在MiniToolMovieMaker中编辑AI视频?免费AI视频剪辑的教程如何在MiniToolMovieMaker中编辑AI视频?免费AI视频剪辑的教程

    MiniTool MovieMaker虽无AI生成功能,但可高效编辑AI生成的MP4、MOV等格式视频或图片序列。通过导入素材后,利用其剪辑、过渡、滤镜、文字、音频处理等功能,实现AI片段的精剪、色彩统一、无缝衔接与风格化输出。支持主流视频、图片及音频格式,兼容性好,适合个人创作者进行AI内容后期整…

    2026年9月22日 用户投稿
    600
  • VSCode如何调试JavaScript代码 VSCode调试功能的实战技巧

    要在vscode中调试javascript,首先需设置断点、配置launch.json文件、选择合适的调试环境并启动调试会话;2. launch.json至关重要,常见陷阱包括program路径错误、type类型不匹配、cwd设置不当、混淆launch与attach模式以及source map配置缺…

    2026年9月22日
    000
  • Linux内核13-进程切换

    进程切换,也称为任务切换、上下文切换或任务调度,本文将探讨linux内核中进程切换的实现。我们首先理解几个关键概念。 1.1 硬件上下文 每个进程都有自己的地址空间,但所有进程共享CPU寄存器。因此,在恢复进程执行前,内核必须确保挂起时的寄存器值被重新加载到CPU寄存器中。 这些需要加载到CPU寄存…

    2026年9月22日
    200
  • 如何修改MySQL的默认端口号?

    如何修改MySQL的默认端口号?如何修改MySQL的默认端口号?如何修改MySQL的默认端口号?如何修改MySQL的默认端口号?

    修改mysql默认端口号需编辑配置文件,核心步骤为:1.定位my.cnf或my.ini文件;2.在[mysqld]段落中修改或添加port参数;3.保存后重启mysql服务。更改端口主要出于避免冲突、提升安全性和适应网络策略考虑。连接时需在客户端工具或代码中指定新端口,如命令行加-p参数、编程语言连…

    2026年9月22日 用户投稿
    1200

发表回复

登录后才能评论
关注微信