__slots__实例详解 |
python是动态语言,在创建了一个class实例后,我们可以给该实例绑定任何属性和方法。如下所示:
>>> class Student(object):
... pass
...
>>> s = Student()
>>> s.name = 'stemon'
>>> print s.name
stemon
>>> def set_score(self, score):
... self.score = score
...
>>> from types import MethodType
>>> s.set_score = MethodType(set_score, s, Student)
>>> s.set_score(100)
>>> print s.score
100
这里的动态绑定是针对类实例的,给一个实例绑定的属性或者方法,对另一个实例是不起作用的:
>>> s2 = Student()
>>> s2.name
Traceback (most recent call last):
File "" , line 1, in
AttributeError: 'Student' object has no attribute 'name'
>>> s2.set_score(100)
Traceback (most recent call last):
File "" , line 1, in
AttributeError: 'Student' object has no attribute 'set_score'
为了给所有的实例都动态绑定该方法,可以给class动态绑定这个方法:
>>> Student.set_score = MethodType(set_score, None, Student)
>>> s2.set_score(100)
>>> print s2.score
100
>>> s.set_score(99)
>>> print s.score
99
通常情况下,类似set_score
这样的方法直接定义在class中,但是这种动态绑定允许我们在程序运行的过程中给class增加功能。
如果我们想要限制python中class的属性,python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class的实例能添加的属性,比如只能添加name和age两个属性:
>>> class Student(object):
... __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
...
然后测试一下:
>>> s.name = 'stemon'
>>> s.age = 25
>>> s.score = 99
Traceback (most recent call last):
File "" , line 1, in
AttributeError: 'Student' object has no attribute 'score'
再动态添加一个方法试试:
>>> s.set_score = MethodType(set_score, s, Student)
Traceback (most recent call last):
File "" , line 1, in
AttributeError: 'Student' object has no attribute 'set_score'
可见,定义一个__slots__之后,向类实例中添加任何不在__slots__值范围之内的属性或者方法都是不可以的。
如果子类没有定义__slots__,那么父类__slots__定义的属性仅对当前类起作用,对继承的子类不起作用。
如果子类也定义了__slots__,那么子类允许定义的属性就是自身的__slots__加上父类的__slots__。
再回到Student的定义:
>>> class Student(object):
... __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
...
不带__slots__的子类的定义:
>>> class GraduateStudent(Student):
... pass
...
>>> g = GraduateStudent()
>>> g.score = 9999
可以看到,不受父类的__slots__的限制
带__slots__的子类的定义:
>>> class GraduateStudent(Student):
... __slots__ = ('score')
...
>>> g = GraduateStudent()
>>> g.name = 'stemon'
>>> g.age = 26
>>> g.score = 9999
>>> g.height = 100
Traceback (most recent call last):
File "" , line 1, in
AttributeError: 'GraduateStudent' object has no attribute 'height'
可见子类如果定义了__slots__便会继承父类的__slots__;如果子类没有定义__slots__,便不会受到父类__slots__的限制。
注意一点__slots__限制的仅仅是类的实例的属性或者方法的动态添加,类本身的属性的添加不受__slots__的限制:
>>> GraduateStudent.width = 100
>>> GraduateStudent.height = 100
>>> print g.height
100
>>> print g.width
100
补充分析:看下面的一个列子
>>> class Student(object):
... __slots__ = ('name', 'age', 'set_score')
...
>>> s = Student()
>>> s.name = 'stemon'
>>> s.age = 25
>>> def set_score(self, score):
... self.score = score
...
>>> from types import MethodType
>>> s.set_score = MethodType(set_score, s, Student)
>>> s.set_score(100)
Traceback (most recent call last):
File "" , line 1, in
File "" , line 2, in set_score
AttributeError: 'Student' object has no attribute 'score'
可以看到,如果在__slots__中定义了方法的名字就可以动态添加方法,但是如果该方法中又定义了其他的不在__slots__中的属性,那么会在该方法调用的时候产生异常。
同时注意,__slots__限制的是类的实例的动态属性或者方法的添加,在类的__init__
等方法中定义的属性都是动态的,都受到__slots__的限制。在__init__
等方法的执行过程中,定义的属性跟终端通过实例增加属性是一样的,都是动态的。
在终端通过实例增加方法,受到__slots__的限制,但是在类的声明中通过def实现的方法是不受__slots__限制的,因为在类的声明中实现的方法是静态的。