33、Python之面向对象:实例对象太占内存,可以试着这样做

南宫理的日志录 2024-10-12 09:23:31
引言

在该系列的上一篇文章中,我们留下了一个问题,Python中一切皆对象,Python对象底层是对dict的弱封装,那么为什么有些内置类找不到__dict__属性呢?似乎,我们前面所介绍的自相矛盾了……

通过今天这篇文章,希望能够自圆其说,并学会基于这种特性,进行实例对象的内存优化。

内置类型对象没有__dict__属性

首先我们通过代码,来验证下内置类型有没有__dict__属性:

对int、str、list、tuple、list等实例对象,访问其__dict__属性,都会报AttributeError的错误,提示对象并没有__dict__的属性。

其实,内置类型的对象,之所以没有__dict__属性,主要是由于它们通常是使用C语言实现的,而且,为了使用性能,有自己特定的内存布局和属性的管理方式。

那么,我们自定义的类型,是否也可以进行内存布局的优化,而不是使用dict方式进行存储呢?

答案是肯定的,这就是我们这篇文章,重点要介绍的Python的特殊属性__slots__。

灵活与性能的Trade-Off

在Python中,我们可以通过__slots__属性来进行自定义类型的内存优化与性能提升。当使用__slots__进行属性的限定之后,该类的实例将不再以默认的字典(dict)方式来存储实例数据,转而采用一种基于数组的更加紧凑的数据结构。

因此,当有场景需要创建大量的实例对象时,可以尝试使用__slots__来显著减少内存占用和程序执行时间。

以实际代码来看__slost__的用法及效果:

代码中,我们使用了sys内置模块的getsizeof()方法,来查看一个对象占用内存的字节数,这里放一下该函数的定义:

执行结果:

从执行结果,可以看出,使用__slot__限定属性之后,内存确实降低了8个字节,具体为什么分别是56和48,是由具体的实现决定的,可以不用在意,我们只需要注意到这个比较优势即可。

此外,使用__slost__之后,__dict__不见了,毕竟换了一种基于数组的实例对象的数据存储方式,不再使用字典了。

而没有了__dict__属性,一个最直接的影响时,不能再动态给实例对象添加属性了。此外,需要注意的是,如果一个类继承自有__slots__属性的类时,如果不声明__slots__属性,又会出现__dict__属性,这时,内存的节省可能就不太有意义了。

直接看代码:

执行结果:

总结

稍微总结一下__slots__属性的特性及需要注意的点:

1、定义__slots__属性后,会将该类的实例对象的数据将不再使用默认的基于字典的方式存储,而采用基于数组的更加紧凑的方式进行存储,内存的占用和执行时间将得到大大消减。

2、一但使用了__slots__属性,将会失去一些动态语言的灵活性,比如不能动态添加新的属性了,所以,具体使用时,需要进行权衡取舍。

3、当在继承中使用__slots__时,需要尤其注意。如果继承自使用__slots__的基类,那么子类也需要定义__slots__来存储自己的属性(即使它不会添加任何属性,也应该如此)。否则,子类的运行速度将更慢,占用的内存也更多,比完全不使用__slots__时还要更糟。

4、使用__slots__将会破坏期望实例具有底层__dict__属性的代码。

5、可以在__slots__属性中,加入__dict__从而让类的实例对象具有__dict__,但是,这样就失去了节省内存的意义。

6、如果没有把__weakref__加入到__slots__中,那么该类的实例对象将不能作为弱引用的目标。

感谢您的拨冗阅读,如果这篇文章对您学习Python有所帮助,欢迎点赞收藏。

0 阅读:15

南宫理的日志录

简介:深耕IT科技,探索技术与人文的交集