Python __slots__的使用方法
作者:忧桑的小兔子 发布时间:2023-11-19 16:15:10
准备
正常情况下,创建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
来源:https://blog.csdn.net/lis_12/article/details/53511300
猜你喜欢
- 记得很早以前看到过这样的一段介绍:想象你在逛街边的一家书店,如果最终你没有购买任何图书就直接离开了,店长并不会知道你来过。但是如果你买了书,
- 生成器就是自己用python代码写的迭代器,生成器的本质就是迭代器。通过以下两种方式构建一个生成器:1、通过生成器函数2、生成器表达式生成器
- 目录1. 前言2. 实战一下2-1 进入虚拟环境,创建一个项目及 App2-2 创建模板目录并配置 set
- 一、数据库操作1、创建model表基本结构:#coding:Utf8from django.db import modelsclass us
- 本文实例讲述了php返回相对时间(如:20分钟前,3天前)的方法。分享给大家供大家参考。具体如下:function plural($num)
- 前言近几日迫于被辅导员三番五次的提醒每日一报打卡,就想着去写个脚本挂在服务器上定时执行。经过我不懈的努力,最终选择了seleniumsele
- 利用Opencv中的Houghline方法进行直线检测—python语言这是给Python部落翻译的文章,请在这里看原文。在图像处理中,霍夫
- 1、Python调用Windows命令打印文件Windows命令行打印文件使用print 命令,具体用法可使用help print查看。下面
- 目录1. 首先2. 编程惯用法3. 基础用法4. 库的使用5. 设计模式6. 内部机制7. 使用工具辅助项目开发8. 性能剖析与优化1. 首
- 再之前同时安装 python 后 只需把环境变量PATH 里面改为PATH=C:\Python36-32\Scripts\;C:\Pytho
- 一、多层前向神经网络多层前向神经网络由三部分组成:输出层、隐藏层、输出层,每层由单元组成;输入层由训练集的实例特征向量传入,经过连接结点的权
- 如何最大限度地实现安全登录功能? 具体方法如下(这是一个程序,为便于说明,中间用虚线“------”将代
- 前言这篇文章主要给大家总结了关于学习Python的新手们容易犯的几个错误,一共四个易犯错误,下面来看看详细的介绍吧。一、i+=1 不等于++
- Python Logging原来真的远比我想象的要复杂很多很多,学习路线堪比git。但是又绕不过去,alternatives又少,所以必须要
- 一、%号占位符这是一种引入最早的一种,也是比较容易理解的一种方式.使用方式为:1、格式化字符串中变化的部分使用占位符2、变量以元组形式提供3
- 今天我升级MYSQL到5.1的时候遇到的。写出来共享以下。1、[root@localhost mysql]# scripts/mysql_i
- 对于大多数web应用来说,数据库都是一个十分基础性的部分。如果你在使用PHP,那么你很可能也在使用MySQL—LAMP系列中举足轻重的一份子
- php高并发之opcache今天工作的时候接触到客户的一台服务器,业务逻辑比较简单 。估算pv在120w左右吧,用的是阿里云2c4g的服务器
- 使用本文提供的JavaScript脚本,配合Dreamweaver的层和行为的运用,可以在页面中显示可拖动的精美月历。具体制作步骤如下:1、
- 1函数是一种有零个或多个参数并且有一个返回值的程序。在SQL中Oracle内建了一系列函数,这些函数都可被称为SQL或PL/SQL语句,函数