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实现在windows服务中新建进程的方法
Jun 30 Python
python编程之requests在网络请求中添加cookies参数方法详解
Oct 25 Python
Python实现翻转数组功能示例
Jan 12 Python
python+pandas+时间、日期以及时间序列处理方法
Jul 10 Python
Python解决走迷宫问题算法示例
Jul 27 Python
Django自定义用户登录认证示例代码
Jun 30 Python
浅谈tensorflow中dataset.shuffle和dataset.batch dataset.repeat注意点
Jun 08 Python
django 装饰器 检测登录状态操作
Jul 02 Python
python 服务器运行代码报错ModuleNotFoundError的解决办法
Sep 16 Python
在pycharm中使用pipenv创建虚拟环境和安装django的详细教程
Nov 30 Python
解决hive中导入text文件遇到的坑
Apr 07 Python
Python基础之pandas数据合并
Apr 27 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图片处理之使用imagecopyresampled函数实现图片缩放例子
2014/11/19 PHP
php使用gzip压缩传输js和css文件的方法
2015/07/29 PHP
thinkphp如何获取客户端IP
2015/11/03 PHP
php自定义截取中文字符串-utf8版
2017/02/27 PHP
Laravel 前端资源配置教程
2019/10/18 PHP
PHP的HTTP客户端Guzzle简单使用方法分析
2019/10/30 PHP
JQuery为textarea添加maxlength属性的代码
2010/04/07 Javascript
Moment.js 不容错过的超棒Javascript日期处理类库
2012/04/15 Javascript
checkbox选中与未选中判断示例
2014/08/04 Javascript
fastclick插件导致日期(input[type=&quot;date&quot;])控件无法被触发该如何解决
2015/11/09 Javascript
javascript检查某个元素在数组中的索引值
2016/03/30 Javascript
浅谈JS中的!=、== 、!==、===的用法和区别
2016/09/24 Javascript
BootStrap实现带有增删改查功能的表格(DEMO详解)
2016/10/26 Javascript
lhgcalendar时间插件限制只能选择三个月的实现方法
2017/07/03 Javascript
vue params、query传参使用详解
2017/09/12 Javascript
五步轻松实现JavaScript HTML时钟效果
2020/03/25 Javascript
vue 集成jTopo 处理方法
2019/08/07 Javascript
vue中利用iscroll.js解决pc端滚动问题
2020/02/15 Javascript
vue页面更新patch的实现示例
2020/03/25 Javascript
详解js中的几种常用设计模式
2020/07/16 Javascript
使用SAE部署Python运行环境的教程
2015/05/05 Python
Python多个装饰器的调用顺序实例解析
2020/05/22 Python
Python3获取cookie常用三种方案
2020/10/05 Python
美国受信赖的教育产品供应商:Nest Learning
2018/06/14 全球购物
英国在线自行车店:Merlin Cycles
2018/08/20 全球购物
印尼在线购买隐形眼镜网站:Lensza.co.id
2019/04/27 全球购物
物流专业大学生的自我鉴定
2013/11/13 职场文书
安全生产检讨书
2014/01/21 职场文书
课程改革实施方案
2014/03/16 职场文书
房屋租赁意向书
2014/04/01 职场文书
主题实践活动总结
2014/05/08 职场文书
党校学习个人总结
2015/02/15 职场文书
寻找成龙观后感
2015/06/12 职场文书
2015初中团支部工作总结
2015/07/21 职场文书
mongodb清除连接和日志的正确方法分享
2021/09/15 MongoDB
Python爬虫网络请求之代理服务器和动态Cookies
2022/04/12 Python