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 tempfile模块学习笔记(临时文件)
May 25 Python
简介Python中用于处理字符串的center()方法
May 18 Python
python 网络编程详解及简单实例
Apr 25 Python
Python编程pygal绘图实例之XY线
Dec 09 Python
uwsgi+nginx部署Django项目操作示例
Dec 04 Python
对python GUI实现完美进度条的示例详解
Dec 13 Python
提升Python程序性能的7个习惯
Apr 14 Python
python实现海螺图片的方法示例
May 12 Python
python爬虫爬取监控教务系统的思路详解
Jan 08 Python
如何基于python对接钉钉并获取access_token
Apr 21 Python
python 实现客户端与服务端的通信
Dec 23 Python
python数据库批量插入数据的实现(executemany的使用)
Apr 30 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 各种排序算法实现代码
2009/08/20 PHP
php模拟socket一次连接,多次发送数据的实现代码
2011/07/26 PHP
web server使用php生成web页面的三种方法总结
2013/10/28 PHP
静态html文件执行php语句的方法(推荐)
2016/11/21 PHP
在PHP 7下安装Swoole与Yar,Yaf的方法教程
2017/06/02 PHP
浅谈PHP中的面向对象OOP中的魔术方法
2017/06/12 PHP
PHP排序算法之直接插入排序(Straight Insertion Sort)实例分析
2018/04/20 PHP
PHP封装的非对称加密RSA算法示例
2018/05/28 PHP
windows系统下简单nodejs安装及环境配置
2013/01/08 NodeJs
解决js数据包含加号+通过ajax传到后台时出现连接错误
2013/08/01 Javascript
改变文件域的样式实现思路同时兼容ie、firefox
2013/10/23 Javascript
javascript 上下banner替换具体实现
2013/11/14 Javascript
jtable列中自定义button示例代码
2013/11/21 Javascript
使用VS开发 Node.js指南
2015/01/06 Javascript
JavaScript判断DIV内容是否为空的方法
2016/01/29 Javascript
详解JavaScript中this关键字的用法
2016/05/26 Javascript
jQuery实现鼠标经过购物车出现下拉框代码(推荐)
2016/07/21 Javascript
javascript操作cookie
2017/01/17 Javascript
JS库 Highlightjs 添加代码行号的实现代码
2017/09/13 Javascript
微信小程序实现留言板
2018/10/31 Javascript
js找出5个数中最大的一个数和倒数第二大的数实现方法示例小结
2020/03/04 Javascript
js实现手表表盘时钟与圆周运动
2020/09/18 Javascript
[02:49]DOTA2完美大师赛首日观众采访
2017/11/23 DOTA
使用django-suit为django 1.7 admin后台添加模板
2014/11/18 Python
css3实现波纹特效、H5实现动态波浪效果
2018/01/31 HTML / CSS
Urban Outfitters英国官网:美国平价服饰品牌
2016/11/25 全球购物
iHerb中文官网:维生素、保健品和健康产品
2018/11/01 全球购物
波兰运动鞋网上商店:Distance.pl
2020/07/30 全球购物
大四自我鉴定
2014/02/08 职场文书
《学会待客》教学反思
2014/02/22 职场文书
幼儿教师师德演讲稿
2014/05/06 职场文书
新书发布会策划方案
2014/06/09 职场文书
法人代表身份证明书及授权委托书
2014/09/16 职场文书
个人务虚会发言材料
2014/10/20 职场文书
vue项目中的支付功能实现(微信支付和支付宝支付)
2022/02/18 Vue.js
Python中的协程(Coroutine)操作模块(greenlet、gevent)
2022/05/30 Python