Python __slots__的使用方法


Posted in Python onNovember 15, 2020

准备

正常情况下,创建class的实例后,可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。首先定义一个class

class A(object):
  pass

然后创建一个实例,并给实例添加属性和方法。

a = A()
print a.__dict__   #{}
A.name = 'xiaoming' #动态的给实例绑定属性,其实例属性会保存到实例的__dict__中
print a.__dict__   #{'name': 'xiaoming'}
f = lambda :100
a.fun = f      
print a.__dict__   #{'fun': <function <lambda> at>, 'name': 'xiaoming'}

此时的name属性和fun()方法只有实例a能使用,类A的其他实例不能使用,如果想让类A的所有实例都能使用,我们需要给类A绑定方法

print A.__dict__   #...
f = lambda :100
A.fun = f      
print A.__dict__   #... + 'fun': <function <lambda> at 0x0000000003582978>

此时,类A的所有实例就能使用方法fun()了。

​ 通常情况下,上面的fun()方法应该定义在class中,但动态绑定允许在程序运行的过程中动态的给class增加功能,这在静态语言中很难实现,这也是动态语言的优点。

__slots__

​ 如果在一个类中定义了__slots__属性,那么这个类的实例将不会拥有__dict__属性,没有__dict__的实例也就不能添加实例属性了。简单来说,__slots__的作用就是阻止类在实例化时为实例分配__dict__属性,限制该实例能添加的属性。

作用

​ 通常情况下实例使用__dict__来存储自己的属性,它允许实例动态地添加或删除属性。然而,对一些在编译期就已经知道有什么变量的类或者不允许动态添加变量的类来说,它们并不需要动态地添加变量。如果想要限制实例属性,不想让它动态添加属性怎么办?比如我们只允许对A的实例添加name和age属性。

​ 为了达到上述目的,Python允许在定义class的时候,定义一个__slots__变量,来限制该class的实例能添加的属性。

class A(object):
  __slots__ = ('age','name')
a = A()
a.name = 'xiaoming'
a.age = 10
a.id = 123456 #error  AttributeError: 'A' object has no attribute 'id'

由于id不在__slots__中,所以实例不能添加id属性。任何试图给实例添加一个其名不在__slots__中的属性都将触发AttributeError异常。

实现原理

__slots__中的变量是类属性,类型为数据描述符。

#!/usr/bin/python
# -*- coding: utf-8 -*-
class Foo(object):
  __slots__ = ('age','name')
  def __init__(self,age = 0):
    self.age = age

s = Foo.__dict__['age']
print s          #<member 'age' of 'Foo' objects>
print type(s)       #<type 'member_descriptor'>
'''证明为数据描述符'''
print '__get__' in dir(s) #True
print '__set__' in dir(s) #True

__slots__中的变量虽然是类属性,但是不同实例之间互不影响。因为描述符方法的一个参数为实例,建立一个实例和值的映射还是很简单的。如果不懂,建议看描述符 。

f1 = Foo(1)
f2 = Foo(2)
print f1.age,f2.age          #1,2
print Foo.__dict__['age'].__get__(f1) #1
print Foo.__dict__['age'].__get__(f2) #2

__slots__的好处

如果类没有定义__slots__ ,该类的实例会有__dict__属性,通过__dict__可修改,删除,增加实例属性。

如果类定义了__slots__,该类的实例不会有__dict__属性。实例中的__dict__属性是非常耗内存的,当创建上百万个实例的时候,所有实例的__dict__会占用一块很大的内存。没有了__dict__的实例也就不能动态添加属性,只需分配固定的空间来存储已知的属性。因此使用__slots__的类能节省一部分内存开销。

对于不需要动态添加属性的类来说,应使用__slots__。

注意:不用过早的使用这个方法,它不利于代码维护,当实例很多(上千万)时,这种优化才有明显的效果。在实际使用中,__slots__从未被当作一种安全的特性来使用,它是对内存和执行速度的一种性能优化。使用__slots__的类的实例不再使用字典来存储实例属性,而是使用基于数组的一种更加紧凑的数据结构,所以当实例很多时,使用__slots__可以显著减少内存占用和执行时间。

class A(object):
  pass

class B(object):
  __slots__ = ('age','name')

a = A()
b = B()

没有__slots__的类和实例

print a.__dict__ #{}
print A.__dict__ 
'''
{'__dict__': <attribute '__dict__' of 'A' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
'''

有__slots__的类和实例

print b.__dict__ #AttributeError: 'B' object has no attribute '__dict__'
print B.__dict__
'''
{'age': <member 'age' of 'B' objects>, '__module__': '__main__', '__doc__': None, '__slots__': ('age', 'name'), 'name': <member 'name' of 'B' objects>}
'''

使用定义__slots__的类的注意事项

class Foo(object):
  __slots__ = ('age','name')

1.__slots__仅对当前类起作用,对子类是不起作用的,除非在子类中也定义__slots__,这样,子类允许定义的属性就是自身的__slots__加上父类的__slots__。

class F1(Foo):
  pass

class F2(Foo):
  __slots__ = ()

f1,f2 = F1(),F2()
f1.a = 1
f2.a = 1    #AttributeError: 'F2' object has no attribute 'a'
f2.age = 1

2.如果实例未给__slots__中的变量赋值,该实例不能使用__slots__中的变量。

f = Foo()
print f.age  #AttributeError: age
f.age = 1
print f.age  #1

3.实例将不再拥有__dict__,但是类还是拥有__dict__属性的,所以还是可以给类增加类属性的;

f = Foo()
Foo.xx = 1
print f.xx   #1

4.定义了__slots__后,如果__slots__中的变量为类变量,该变量对于该类的实例来说是只读的。如果想修改的话,可以通过类来修改。

class Foo(object):
  __slots__ = ('age','name')
  age = 10
  def __init__(self):
    self.name = 'xiaoming'
f = Foo()
print f.name     #'xiaoming'
print f.age     #10
f.name = 'xiaohong' 

#f.age = 12     #AttributeError: 'Foo' object attribute 'age' is read-only

Foo.age = 12     #正确
print f.name     #'xiaohong'
print f.age     #12


#del f.age      #AttributeError: 'Foo' object attribute 'age' is read-only


#del f.name 调用的是描述符方法__delete__,应该是将描述符中存储的实例与值的映射删除了

del f.name      #实例属性的删除还是可以的,但是删除的并不是类字典中的name属性
print f.name     #AttributeError: name

del Foo.age     #通过类来删除类属性还是可以的,不过这样会影响该类的所有实例
print f.age     #AttributeError: 'Foo' object has no attribute 'age'

原因不知道…有知道的大神求指导…

参考网址
1.http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/0013868200605560b1bd3c660bf494282ede59fee17e781000
2.http://www.jianshu.com/p/ef1797577f71
3.http://www.jianshu.com/p/82ce2151d73b
4.http://blog.csdn.net/lis_12/article/details/53453665

到此这篇关于Python __slots__的使用方法的文章就介绍到这了,更多相关Python __slots__内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
python批量导出导入MySQL用户的方法
Nov 15 Python
web.py在模板中输出美元符号的方法
Aug 26 Python
Python统计日志中每个IP出现次数的方法
Jul 06 Python
Python基于正则表达式实现检查文件内容的方法【文件检索】
Aug 30 Python
python使用wxpy实现微信消息防撤回脚本
Apr 29 Python
python集合的创建、添加及删除操作示例
Oct 08 Python
如何通过Django使用本地css/js文件
Jan 20 Python
Jupyter notebook 远程配置及SSL加密教程
Apr 14 Python
Pycharm激活方法及详细教程(详细且实用)
May 12 Python
如何导出python安装的所有模块名称和版本号到文件中
Jun 05 Python
Python读取xlsx数据生成图标代码实例
Aug 12 Python
搭建pypi私有仓库实现过程详解
Nov 25 Python
Python descriptor(描述符)的实现
Nov 15 #Python
基于OpenCV的网络实时视频流传输的实现
Nov 15 #Python
彻底解决Python包下载慢问题
Nov 15 #Python
Python eval函数原理及用法解析
Nov 14 #Python
Django怎么在admin后台注册数据库表
Nov 14 #Python
通过实例解析python and和or使用方法
Nov 14 #Python
Python高并发和多线程有什么关系
Nov 14 #Python
You might like
PHP中文件读、写、删的操作(PHP中对文件和目录操作)
2012/03/06 PHP
微信营销平台系统?刮刮乐的开发
2014/06/10 PHP
php验证身份证号码正确性的函数
2016/07/20 PHP
prototype 中文参数乱码解决方案
2009/11/09 Javascript
JavaScript通过元素索引号删除数组中对应元素的方法
2015/03/18 Javascript
jQuery实现动画效果circle实例
2015/08/06 Javascript
javascript实现禁止复制网页内容汇总
2015/12/30 Javascript
基于jQuery插件实现点击小图显示大图效果
2016/05/11 Javascript
JS不完全国际化&amp;本地化手册 之 理论篇
2016/09/27 Javascript
小程序开发实战:实现九宫格界面的导航的代码实现
2017/01/19 Javascript
JS模拟超市简易收银台小程序代码解析
2017/08/18 Javascript
基于vue-element组件实现音乐播放器功能
2018/05/06 Javascript
基于node搭建服务器,写接口,调接口,跨域的实例
2018/05/13 Javascript
vue 中滚动条始终定位在底部的方法
2018/09/03 Javascript
vuex如何重置所有state(可定制)
2019/01/17 Javascript
Bootstarp在pycharm中的安装及简单的使用方法
2019/04/19 Javascript
Vue + Element UI图片上传控件使用详解
2019/08/20 Javascript
vue路由权限校验功能的实现代码
2020/06/07 Javascript
微信小程序地图实现展示线路
2020/07/29 Javascript
js实现删除json中指定的元素
2020/09/22 Javascript
swiper4实现移动端导航栏tab滑动切换
2020/10/16 Javascript
Node.js 中如何收集和解析命令行参数
2021/01/08 Javascript
vue使用lodop打印控件实现浏览器兼容打印的方法
2021/02/07 Vue.js
Python多线程编程(六):可重入锁RLock
2015/04/05 Python
分享6个隐藏的python功能
2017/12/07 Python
详解python中的index函数用法
2019/08/06 Python
python-Web-flask-视图内容和模板知识点西宁街
2019/08/23 Python
python实现excel公式格式化的示例代码
2020/12/23 Python
CSS3实现王者荣耀匹配人员加载页面的方法
2019/04/16 HTML / CSS
Ramy Brook官网:美国现代女装品牌
2019/06/18 全球购物
美国最大和最受信任的二手轮胎商店:Bestusedtires.com
2020/06/02 全球购物
优秀部门获奖感言
2014/02/14 职场文书
新学期决心书
2014/03/11 职场文书
学习张丽丽心得体会
2014/09/03 职场文书
六查六看自查报告
2014/10/14 职场文书
macos系统如何实现微信双开? mac登录两个微信以上微信的技巧
2022/07/23 数码科技