python通过__slots__限制类实例动态的增加属性、方法

__slots__实例详解

python是动态语言,在创建了一个class实例后,我们可以给该实例绑定任何属性和方法。如下所示:

一、python的动态性

>>> 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增加功能。

二、__slots__的使用

如果我们想要限制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__加上父类的__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

四、__slots__动态添加方法的疑问

补充分析:看下面的一个列子

>>> 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__限制的,因为在类的声明中实现的方法是静态的。

你可能感兴趣的:(Python学习总结)