1、动态绑定属性和方法
正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。
#给对象绑定属性
class Student(object):
def __init__(self):
self.age=10
self.score=90
s1 = Student()
s2 = Student()
s1.name = "liu"
print(s1.name)
#给对象绑定方法,但是只对当前实例起作用
def set_age(self,age):
self.age = age
from types import MethodType
s1.set_age = MethodType(set_age,s1)
s1.set_age(25)
print(s1.age)
#给所有的实例都绑定方法
def set_score(self,score):
self.score = score
Student.set_score = set_score
s2.set_score(100)
s1.set_score(200)
print("s2.score=%d" % s2.score)
print("s1.score=%d" % s1.score)
2、限制实例的属性
比如,只允许对Student实例添加name和age属性。为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性。
class Student(object):
__slots__ = ('name','age')
s =Student()
s.name = 'liu'
s.age = 25
print(s.name)
print(s.age)
s.score = 99
但是要注意的是通过__slots__限制的属性只在父类种有效,在子类种是不起作用的。
如下所示:
class Student(object):
__slots__ = ('name','age')
s =Student()
s.name = 'liu'
s.age = 25
print(s.name)
print(s.age)
class GraduaeStudent(Student):
pass
g = GraduaeStudent()
g.score=100
print(g.score)
除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__
3、使用@property
在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改。而@property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。如下:
class Student(object):
pass
s1 = Student()
s1.score = 100
print(s1.score)
这显然不合逻辑。为了限制score的范围,可以通过一个set_score()方法来设置成绩,再通过一个get_score()来获取成绩,这样,在set_score()方法里,就可以检查参数:
class Student(object):
def get_score(self):
return self._score
def set_score(self,value):
if not isinstance(value,int):
raise ValueError('score must be the integer')
if value <0 or value >100:
raise ValueError('score must between 0-100')
self._score = value
s1 = Student()
s1.set_score(100)
print(s1.get_score())
但是上面的写法有点复杂,对外使用者而言还是不够完美,所以我们可以使用@property,Python内置的@property装饰器可以把一个方法变成属性调用。展示如下:
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self,value):
if not isinstance(value,int):
raise ValueError('the score must be integer')
if value < 0 or value > 100:
raise ValueError('the score must between 0-100')
self._score = value
s =Student()
s.score = 101
下面的结果表示,对参数进行了检查。
所以我们注意到这个@property的时候,我们在对实例属性操作的时候,就知道该属性很可能不是直接暴露的,而是通过getter和setter方法来实现的。当我们没有设置setter的时候,就相当于只定义了只读属性。演示如下:
class Student(object):
@property
def birth(self):
return self._birth
@birth.setter
def birth(self,value):
self._birth = value
@property
def age(self):
return 2020-self._birth
s =Student()
s.birth = 1995
print(s.birth)
print(s.age)
s.age = 25