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实现的去除win下文本文件头部BOM的代码
Feb 10 Python
Python通过调用mysql存储过程实现更新数据功能示例
Apr 03 Python
pycharm 将django中多个app放到同个文件夹apps的处理方法
May 30 Python
Python中存取文件的4种不同操作
Jul 02 Python
python画图把时间作为横坐标的方法
Jul 07 Python
Python3并发写文件与Python对比
Nov 20 Python
python离线安装外部依赖包的实现
Feb 13 Python
使用pandas库对csv文件进行筛选保存
May 25 Python
Python正则表达式如何匹配中文
May 27 Python
python opencv把一张图片嵌入(叠加)到另一张图片上的实现代码
Jun 11 Python
Matplotlib 折线图plot()所有用法详解
Jul 28 Python
python中实现词云图的示例
Dec 19 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 EOT定界符的使用详解
2008/09/30 PHP
php5 non-thread-safe和thread-safe这两个版本的区别分析
2010/03/13 PHP
linux下编译安装memcached服务
2014/08/03 PHP
thinkphp浏览历史功能实现方法
2014/10/29 PHP
PHP实现的线索二叉树及二叉树遍历方法详解
2016/04/25 PHP
PHP获取访问设备信息的方法示例
2019/02/20 PHP
PHP7内核之Reference详解
2019/03/14 PHP
js中获取事件对象的方法小结
2011/03/13 Javascript
遍历jquery对象的代码分享
2011/11/02 Javascript
javascript计算当月剩余天数(天数计算器)示例代码
2014/01/09 Javascript
jQuery实现复选框全选/取消全选/反选及获得选择的值
2014/06/12 Javascript
分享经典的JavaScript开发技巧
2015/11/21 Javascript
jQuery打字效果实现方法(附demo源码下载)
2015/12/18 Javascript
JavaScript必知必会(十) call apply bind的用法说明
2016/06/08 Javascript
整理关于Bootstrap导航的慕课笔记
2017/03/29 Javascript
微信小程序使用Socket的实例
2017/09/19 Javascript
angular-tree-component的使用详解
2018/07/30 Javascript
关于js陀螺仪的理解分析
2019/04/11 Javascript
Async/Await替代Promise的6个理由
2019/06/15 Javascript
JavaScript 常见的继承方式汇总
2020/09/17 Javascript
Python中修改字符串的四种方法
2018/11/02 Python
python中退出多层循环的方法
2018/11/27 Python
pandas DataFrame 警告(SettingWithCopyWarning)的解决
2019/07/23 Python
python读取tif图片时保留其16bit的编码格式实例
2020/01/13 Python
jupyter notebook更换皮肤主题的实现
2021/01/07 Python
adidas菲律宾官网:adidas PH
2020/02/07 全球购物
简单英文演讲稿
2014/01/01 职场文书
给朋友的道歉信
2014/01/09 职场文书
幼儿运动会邀请函
2014/01/17 职场文书
文员的职业生涯规划发展方向
2014/02/08 职场文书
关爱女孩行动实施方案
2014/03/13 职场文书
2015年大学生入党自荐书
2015/03/24 职场文书
利用javaScript处理常用事件详解
2021/04/14 Javascript
浅谈TypeScript 索引签名的理解
2021/10/16 Javascript
Html5获取用户当前位置的几种方式
2022/01/18 HTML / CSS
Python获取指定日期是"星期几"的6种方法
2022/03/13 Python