本文字数:2634 字

阅读本文大概需要:7 分钟

写在之前

围绕类的话题,说是说不完的,仅在特殊方法,除了我们在前面遇到过的 __init__(),__new__(),__str__() 等之外还有很多。虽然它们只是在某些特殊的场景中才会用到,但是学会它们却可以成为你熟悉这门语言路上的铺路石。

所以我会在试图介绍一些「黑魔法」,让大家多多感受一下 Python 的魅力所在,俗话说「艺多不压身」就是这个道理了。

内存优化

首先先让我们从复习前面的类属性和实例属性的知识来引出另一个特殊方法:

>>> classSample:... name = 'rocky'...

就像前面的文章我们所说的,每个类都有一个 __dict__() 属性,它包含了当前类的类属性:

>>> Sample.__dict__mappingproxy({'__module__': '__main__', 'name': 'rocky', '__dict__': , '__weakref__': , '__doc__': None})>>> Sample.name'rocky'

同样,如果我们创建了实例,每个实例也有一个 __dict__ 属性,它里面就是当前的实例属性:

>>> a = Sample()>>> a.__dict__{}>>> a.age = 23>>> a.__dict__{'age': 23}

上面的操作可以看出,当实例刚刚创建的时候,__dict__ 是空的,只有创建了实例属性以后,它才包含其内容。实例的 __dict__ 和类的 __dict__ 是有所区别的,即实例属性和类属性是不同的。

从理论上来说,我们可以根据一个类创建无数的实例,新建一个实例以后,又创建了一个新的 __dict__,这将是一个很可怕的事情,虽然每个 __dict__ 所占的内存空间很小,当然这件事事实上是不会出现的。但是程序不能建立在这种不可靠的猜测的基础上,程序要对过程有明确的控制。

所以就要有一种方法能够控制 __dict__,于是「__slots__」应运而生。

>>> classNature:... __slots__ = ('tree','flower')... >>> dir(Nature)['__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__', 'flower', 'tree']

我们仔细来看 dir() 的结果,发现 __dict__ 属性没有了,也就是说 __slots__ 把 __dict__ 挤出去了,它进入了类的属性。

>>> Nature.__slots__('tree', 'flower')

从这里可以看出,类 Nature 有且仅有两个属性。从类的角度来看,其类属性只有这两个;从实例的角度来看,其实例属性也只有这两个。

>>> Nature.tree = 'liushu'>>> Nature.tree'liushu'>>> Nature.tree = 'lishu'>>> Nature.tree'lishu'

通过类可以对属性进行赋值和修改,这个似乎和以前的类属性没有什么区别,别着急,继续往下看就看到区别了:

>>> x = Nature()>>> x.__slots__('tree', 'flower')>>> y = Nature()>>> y.__slots__('tree', 'flower')>>> id(x.__slots__)4531629384>>> id(y.__slots__)4531629384

你看,实例化以后,实例的 __slots__ 和类的 __slots__ 完全一样,这跟前面的 __dict__ 大不一样了。并且我们建立了两个实例,结果发现两个实例的 __slots__ 在内存中居然是一个,或者可以说是增加实例时 __slots__ 并不增加。

>>> x.tree'lishu'>>> y.tree'lishu'

既然类属性已经赋值,那么通过任何一个实例属性都能得到同样的值,不过这时候不能通过实例修改此属性的值。

>>> x.tree = 'taoshu'Traceback (most recent calllast): File "", line1, in AttributeError: 'Nature' object attribute 'tree'isread-only

对实例属性来说,类的静态数据是只读的,不能修改,只有通过类属性才能修改。但对于尚未赋值的属性,能够通过实例赋值。

>>> x.flower = 'rose'>>> x.flower'rose'>>> x.flower = 'moli'

显然通过实例操作的属性,也能够通过实例修改,但是实例属性的值并不能够修改类属性的值

Nature.flower

由上面可以看出,实例属性的值并没有传回给类属性,也可以理解为新建了一个同名字的实例属性,如果再给类属性赋值的话,则会像下面一样:

>>> Nature.flower = 'huaihua'>>> x.flower'huaihua'

类属性对实例属性具有决定作用,对实例而言,通过类所定义的属性都是只读的。

__slots__ 已经把实例属性牢牢的看管起来,只能是指定的属性,如果想要增加属性的话,只能通过类属性来实现,所以 __slots__ 的一个重要作用就是优化了内存。

写在之后

当然了,__slots__ 还能加快属性加载速度,这个不是本文的重点,所以不做过多的介绍,感兴趣的可以去 Google 一下。

今天的文章就到这里啦,明天讲一下「黑魔法之属性拦截」,又是新的一周,燥起来!

如果你觉得文章对你有帮助的话,欢迎点赞转发,让更多的人看到,谢谢啦。

The end。

python 内存优化_Python 黑魔法之内存优化相关推荐

  1. c python 内存冲突_Python在计算内存时应该注意的问题?

    我之前的一篇文章,带大家揭晓了 Python 在给内置对象分配内存时的 5 个奇怪而有趣的小秘密.文中使用了sys.getsizeof()来计算内存,但是用这个方法计算时,可能会出现意料不到的问题. ...

  2. python的内存泄露_Python 程序的内存泄露,教你一招来解决?

    如果大家在 Linux 或者 macOS 下面运行一段可能导致内存泄露的程序,那么你可能会看到下面这样的情况: 而如果你用的系统是 Windows,那么可能电脑直接就卡死了. 但是,调试这种 OOM( ...

  3. python 组合优化_python基于粒子群优化的投资组合优化研究

    我今年的研究课题是使用粒子群优化(PSO)的货币进位交易组合优化.在本文中,我将介绍投资组合优化并解释其重要性.其次,我将演示粒子群优化如何应用于投资组合优化.第三,我将解释套利交易组合,然后总结我的 ...

  4. python的内存机制_python中的内存机制

    首先要明白对象和引用的概念 (例子:a=1, a为引用,1为对象,对象1的引用计数器为1,b=1此时内存中只有一个对象1,a,b都为引用,对象的引用计数器此时为2,因为有两个引用) a=1,b=1id ...

  5. python内存清理_python如何清理内存

    引用计数,这是 Python 的垃圾回收策略.补充一下. 解释器(也就是你说的 Shell)负责跟踪对象的引用计数,垃圾收集器负责释放内存. 如何释放?可以通过销毁对象的引用,使引用计数减少至 0.假 ...

  6. python的内存管理_python如何管理内存?

    介绍 内存管理是有效分配,重新分配和协调内存的过程,以便所有不同的进程都能平稳运行并可以最佳地访问不同的系统资源.内存管理还涉及清除不再访问的对象的内存. 在Python中,内存管理器通过定期运行以清 ...

  7. python嵌套循环效率_Python嵌套循环数组比较优化的可能性?

    我试图优化一个嵌套的for循环,将数组中的一个元素与数组中的其余元素进行比较.在 有两部分,第一部分是例如,一个数组有3个元素,每个元素都是一个字典: [{"someKey_1": ...

  8. python判断不等_Python黑魔法笔记第六关:消灭该死的重复(下)

    上一关我们学习了for循环和while循环,让我们复习回顾一下: 然后也对比了什么是适合用for循环,什么时候适合用while循环: 这一关我们的任务是更深入的学习循环语句的用法: 我们还是接着上一关 ...

  9. python太占内存_Python占用的内存优化教程

    概述 如果程序处理的数据比较多.比较复杂,那么在程序运行的时候,会占用大量的内存,当内存占用到达一定的数值,程序就有可能被操作系统终止,特别是在限制程序所使用的内存大小的场景,更容易发生问题.下面我就 ...

最新文章

  1. git clone从远程主机克隆一个版本库
  2. python matplotlab.pyplot.axis()函数的用法
  3. linux版本 如何查kali_000_Kali Linux版本查看和apt源配置
  4. PageHelper分页时超过最大数量的页数仍然返回数据,PageHelper分页失效
  5. pthread异步_异步管道的实现
  6. boost boost::asio::read socket.read_some 区别
  7. linux查找某个命令属于哪个rpm包
  8. ( 4 )MySQL中的数据类型(字符串类型)
  9. codeforces Div.2(5.21)B题
  10. SPICAN通信协议简介
  11. 有一个测试微信删除软件叫wool,微信中用发起群聊来测试你有没有被对方删除好友方法图文教程...
  12. android 钛备份,钛备份使用教程
  13. 最实用的计算机系统清理加速,最实用的win7电脑清理垃圾方法分享
  14. FortiGate 流量整形限速
  15. java 九九乘法口诀
  16. js插入浏览器实现自动点击按钮
  17. iOS小技能: 开发 uni 原生插件(支持iOS Extension)
  18. Unreal Engine 4 系列教程 Part 7:音频教程
  19. 递归算法及经典递归实现
  20. 阿里格灵深瞳计算机视觉岗实习面经

热门文章

  1. SQL Server 2008空间数据应用系列五:数据表中使用空间数据类型
  2. Android-JNI开发系列《十一》实践-利用Android C源码实现GIF图片的播放
  3. npm install -g cnpm --registry=https://registry.npm.taobao.org报警告
  4. Win10关闭系统自动更新
  5. Linux音频驱动开发概括
  6. 一个简单的Hook demo
  7. Git/Repo/Gerrit区别
  8. android上层应用apk到G-sensor driver的大致流程
  9. android 不生成odex文件方法
  10. Ubuntu 12.04忘记登录密码及修改密码