您的位置:首页 > 编程语言 > Python开发

17 Python __slots__

2016-12-07 22:21 183 查看

Python

Python
准备

__slots__
作用

实现原理

__slots__的好处

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

参考网址

转载请标明出处(http://blog.csdn.net/lis_12/article/details/53511300).

准备

正常情况下,创建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')


__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


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

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


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

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


定义了
__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'


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

参考网址

http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/0013868200605560b1bd3c660bf494282ede59fee17e781000

http://www.jianshu.com/p/ef1797577f71

http://www.jianshu.com/p/82ce2151d73b

http://blog.csdn.net/lis_12/article/details/53453665
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息