准备
正常情况下,创建class的实例后,可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。首先定义一个class
class A(object): pass
然后创建一个实例,并给实例添加属性和方法。
a = A() print a.__dict__ #{} A.name = 'xiaoming' #动态的给实例绑定属性,其实例属性会保存到实例的__dict__中 print a.__dict__ #{'name': 'xiaoming'} <mark>本文来源gaodaimacom搞#代%码@网-</mark>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