__slot__


slot(上)

python中的new-style class要求继承Python中的一个内建类型, 一般继承object,也可以继承list或者dict等其他的内建类型。
在python新式类中,可以定义一个变量__slots__,它的作用是阻止在实例化类时为实例分配dict,
默认情况下每个类都会有一个dict,通过__dict__访问,这个dict维护了这个实例的所有属性,举例如下:

class base(object):
    var=9 #类变量
    def __init__(self):
        pass
 
b=base()
print b.__dict__
b.x=2 #添加实例变量
print b.__dict__
运行结果:
{ }
{'x': 2}

可见:实例的dict只保持实例的变量,对于类的属性是不保存的,类的属性包括变量和函数。
由于每次实例化一个类都要分配一个新的dict,因此存在空间的浪费,因此有了__slots__。
__slots__是一个元组,包括了当前能访问到的属性。
当定义了slots后,slots中定义的变量变成了类的描述符,相当于java,c++中的成员变量声明,
类的实例只能拥有slots中定义的变量,不能再增加新的变量。注意:定义了slots后,就不再有dict。如下:

class base(object):
    __slots__=('x')
    var=8
    def __init__(self):
        pass
 
b=base()
b.x=88 #添加实例变量
print b.x
#b.y=99 #无法添加slots之外的变量 (AttributeError: 'base' object has no attribute 'y')
#print b.__dict__ #定义了__slots__后,就不再有__dict__ (AttributeError: 'base' object has no attribute '__dict__')
运行结果:
88

如果类变量与slots中的变量同名,则该变量被设置为 readonly!!!如下:

class base(object):
    __slots__=('y')
    y=22 # y是类变量,y与__slots__中的变量同名
    var=11
    def __init__(self):
        pass
    
b=base()
print b.y
print base.y
#b.y=66 #AttributeError: 'base' object attribute 'y' is read-only
运行结果:
22
22

Python是一门动态语言,可以在运行过程中,修改实例的属性和增删方法。一般,任何类的实例包含一个字典__dict__,
Python通过这个字典可以将任意属性绑定到实例上。有时候我们只想使用固定的属性,而不想任意绑定属性,
这时候我们可以定义一个属性名称集合,只有在这个集合里的名称才可以绑定。__slots__就是完成这个功能的。

class test_slots(object):
    __slots__='x','y'
    def printHello(self):
        print 'hello!'
 
class test(object):
    def printHello(self):
        print 'hello'
 
print dir(test_slots) #可以看到test_slots类结构里面包含__slots__,x,y
print dir(test)#test类结构里包含__dict__
print '**************************************'
ts=test_slots()
t=test()
print dir(ts) #可以看到ts实例结构里面包含__slots__,x,y,不能任意绑定属性
print dir(t) #t实例结构里包含__dict__,可以任意绑定属性
print '***************************************'
ts.x=11 #只能绑定__slots__名称集合里的属性
t.x=12 #可以任意绑定属性
print ts.x,t.x
ts.y=22 #只能绑定__slots__名称集合里的属性
t.y=23  #可以任意绑定属性
print ts.y,t.y
#ts.z=33 #无法绑定__slots__集合之外的属性(AttributeError: 'test_slots' object has no attribute 'z')
t.z=34 #可以任意绑定属性
print t.z 

运行结果:

['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', ' __slots__', '__str__', '__subclasshook__', 'printHello', ' x', 'y']
['__class__', '__delattr__', ' __dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'printHello']
**************************************
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', ' __slots__', '__str__', '__subclasshook__', 'printHello', ' x', 'y']
['__class__', '__delattr__', ' __dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'printHello']
***************************************
11 12
22 23
34

正如上面所说的,默认情况下,Python的新式类和经典类的实例都有一个 dict来存储实例的属性。这在一般情况下还不错,而且非常灵活,
乃至在程序中可以 随意设置新的属性。但是,对一些在”编译”前就知道有几个固定属性的小class来说,这个dict就有点浪费内存了。
当需要创建大量实例的时候,这个问题变得尤为突出。一种解决方法是在 新式类中定义一个__slots__属性。
__slots__声明中包含若干实例变量,并为每个实例预留恰好足够的空间来保存每个变量;这样Python就不会再使用dict,从而节省空间。
【使用memory_profiler模块,memory_profiler模块是在逐行的基础上,测量代码的内存使用率。尽管如此,它可能使得你的代码运行的更慢。使用装饰器@profile来标记哪个函数被跟踪。】

下面,我们看一个例子:

from  memory_profiler import profile
class A(object): #没有定义__slots__属性
    def __init__(self,x):
        self.x=x
 
@profile
def main():
    f=[A(523825) for i in range(100000)]
 
if __name__=='__main__':
    main()

运行结果,如下图:

在这里插入图片描述

第2列表示该行执行后Python解释器的内存使用情况, 第3列表示该行代码执行前后的内存变化。
在没有定义__slots__属性的情况下,该代码共使用了20.8MiB内存。
从结果可以看出,内存使用是以MiB为单位衡量的,表示的mebibyte(1MiB = 1.05MB)

from  memory_profiler import profile
class A(object):#定义了__slots__属性
    __slots__=('x')
    def __init__(self,x):
        self.x=x
 
@profile
def main():
    f=[A(523825) for i in range(100000)]
 
if __name__=='__main__':
    main()

运行结果,如下图:
在这里插入图片描述

可以看到,在定义了__slots__属性的情况下,该代码共使用了6.1MiB内存,比上面的20.8MiB节省了很多内存!
综上所述,在确定了 类的属性固定的情况下,可以 使用__slots__来优化内存。
提醒:不要贸然进行这个优化,把它用在所有地方。这种做法不利于代码维护,而且只有生成数以千计的实例的时候才会有明显效果。

slot(下)

正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。

from types import MethodType
class student(object):#先定义类student
    pass
 
s=student()
s.name='python' #动态给实例绑定一个属性
print 's.name=',s.name
def set_age(self,age):#定义一个函数(作为实例方法)
    self.age=age
s.set_age=MethodType(set_age,s,student)#给实例绑定一个方法    
s.set_age(26) #调用实例方法
print 's.age=',s.age  #测试结果
#注意:给一个实例绑定的变量和方法,对另一个实例不起作用
#为实例s绑定的变量name和方法set_age()对另一个实例s2不起作用
s2=student()
#print s2.name#AttributeError: 'student' object has no attribute 'name'   
#s2.set_age(23)#AttributeError: 'student' object has no attribute 'set_age' 
#若将变量和方法绑定到class上,则所有的实例均可访问
def set_score(self,score):#定义一个函数(绑定到类上)
    self.score=score
#绑定一个方法到class上,绑定之后,所有的实例均可调用
student.set_score=MethodType(set_score,None,student)
s.set_score(88)
s2.set_score(99)
print s.score,s2.score
运行结果:
s.name= python
s.age= 26
88 99

通常情况下,上面的set_score方法可以直接定义在class中,但动态绑定允许我们在程序运行的过程中动态给class加上功能,
这在静态语言中很难实现。
但是,如果我们想要限制class的属性怎么办?比如,只允许对student类的实例添加name和age属性。
为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class能添加的属性,如下:

class student(object):  
    __slots__=('name','age')#用tuple定义允许绑定的属性名称       
  
s=student()#创建新的实例  
s.name='python'  
s.age=23  
print 's.name=',s.name,'s.age=',s.age  
#s.score=99 #AttributeError: 'student' object has no attribute 'score'     
''''' 

由于’score’没有被放到__slots__中,所以不能绑定score属性,试图绑定score将得到AttributeError的错误。
使用__slots__要注意,__slots__定义的属性仅对当前类起作用,对其子类是不起作用的!
也就是说,我们可以为子类实例动态绑定任意变量和方法 ,如下:

'''  
class graduateStudent(student):  
    pass  
g=graduateStudent()  
print dir(g) #可以看到,实例g结构中包含__dict__属性,因此可以为实例g任意的添加属性!
g.score=99#动态给实例绑定一个属性  
print g.score  
运行结果:
s.name= python s.age= 23
['__class__', '__delattr__', ' __dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name']
99

如果父类和子类中都定义__slots__,这样,子类允许定义的属性就是 自身的__slots__加上父类的__slots__。如下:

class student(object):
    __slots__=('name','age')#用tuple定义允许绑定的属性名称     
 
class graduateStudent(student):
    __slots__=('x','y')
 
g=graduateStudent()

#子类允许定义的属性就是自身的__slots__加上父类的__slots__
g.name='python'
g.age=23
g.x=11
g.y=33
print g.name,g.age,g.x,g.y
#g.score=100 #AttributeError: 'graduateStudent' object has no attribute 'score'
运行结果:
python 23 11 33
class A(object):
    pass
class B(A):
    __slots__=('x')
    
print dir(A)#可以看到A类有一个__dict__属性
print dir(B)#B类也有一个__dict__属性(继承了父类A中的__dict__属性)  
b=B()
b.x=9
print 'b.x=',b.x
b.y=8 #B类包含了__dict__属性,其实例可以任意添加属性
print 'b.y=',b.y
运行结果:
['__class__', '__delattr__', ' __dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
['__class__', '__delattr__', ' __dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '__weakref__', 'x']
b.x= 9
b.y= 8
class A(int):
    __slots__=('x')
 
 
print dir(int)#可以看到,int类不包含__dict__属性(python中的内建类型不包含__dict__属性)
print dir(A)#A类也不包含__dict__属性,但包含__slots__属性(因此A类的实例可以添加的属性局限于__slots__)
a=A()
a.x=78
print 'a.x=',a.x
#a.y=98 #AttributeError: 'A' object has no attribute 'y'
运行结果:
['__abs__', '__add__', '__and__', '__class__', '__cmp__', '__coerce__', '__delattr__', '__div__', '__divmod__', '__doc__', '__float__', '__floordiv__', '__format__', '__getattribute__', '__getnewargs__', '__hash__', '__hex__', '__index__', '__init__', '__int__', '__invert__', '__long__', '__lshift__', '__mod__', '__mul__', '__neg__', '__new__', '__nonzero__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'imag', 'numerator', 'real']
['__abs__', '__add__', '__and__', '__class__', '__cmp__', '__coerce__', '__delattr__', '__div__', '__divmod__', '__doc__', '__float__', '__floordiv__', '__format__', '__getattribute__', '__getnewargs__', '__hash__', '__hex__', '__index__', '__init__', '__int__', '__invert__', '__long__', '__lshift__', '__mod__', '__module__', '__mul__', '__neg__', '__new__', '__nonzero__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', ' __slots__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'imag', 'numerator', 'real', 'x']
a.x= 78

综上所述:
如果 父类存在__dict__属性,则其子类将继承__dict__;此时,即使该子类包含了__slots__属性, 该子类的实例依然可以任意添加变量。

(完)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值