实例属性通过self或实例变量来定义,注意__init__方法中定义的是实例属性:
class Student(object): def __init__(self, name): self.name = name s1 = Student('Bob') #给实例添加一个属性 s1.score = 90 s2 = Student('Jim')
在实例s1中添加一个属性score, 该属性只对s1有用,在s2中是没有的:
>>> s1.score 90 >>> s2.score Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'score'
类属性是怎么定义的呢?
class Student(object): name = 'Student'
当我们定义一个类属性后,这个属性归类所有,所有的实例都可以访问它:
>>> s1 = Student() >>> s2 = Student() >>> s1.name, s2.name ('Student', 'Student')
现在修改一个s1的name, 会出现什么情况呢?
>>> s1.name = 'windows' >>> s1.name 'windows' >>> s2.name 'Student'
再删除s1的那么属性:
>>> del s1.name >>> s1.name 'Student'
也可以删除类的属性,类属性删除后,所有实例都将不能访问该属性了:
>>> del Student.name >>> s1.name Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'name' >>> s2.name Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'name'
同样的,也可以给实例和类绑定一个方法:
class Student(object): pass def set_age(self, age): # 定义一个函数作为实例方法 self.age = age >>> s = Student() >>> from types import MethodType >>> s.set_age = MethodType(set_age, s) # 给实例绑定一个方法 >>> s.set_age(25) # 调用实例方法 >>> s.age # 测试结果 25
给实例绑定的方法,对另一个实例是不生效的;如果要给所有实例绑定方法,可以在类上绑定:
def set_score(self, score): self.score = score Student.set_score = set_score
给类绑定方法后,所有的实例都可以访问;一般情况下,方法可以直接写在类中,但动态绑定允许程序在运行过程中动态添加功能。
但有时候我们不希望对实例随意添加属性或方法,比如我们希望只允许对实例添加age和name属性;Python允许在定义class的时候,定义一个特殊的__slots__
变量,来限制该class实例能添加的属性:
class Student(object): __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
但要注意的是,__slots__只对当前类实例起作用,对子类是不起作用的;除非在子类中也定义__slots__; 这样子类允许定义的属性就是子类__slots__ + 父类__slots__
如果在Student类中有个score属性,那么通过Student.score来访问的时候,就暴露了score属性;知道了score属性,就可以随意对它进行修改,而且不能进行正确性检测,比如把成绩修改成abc,显然是不合适的,要解决这个问题,可以设置一个方法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 an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value
那有没有办法通过Student.score来访问,又有正确性检测呢? 那就需要用到@property装饰器,来把方法变成属性:
class Student(object): @property def score(self): return self._score @score.setter def score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value
这样就可以像属性一样操作了:
>>> s = Student() >>> s.score = 60 # OK,实际转化为s.set_score(60) >>> s.score # OK,实际转化为s.get_score() 60 >>> s.score = 9999 Traceback (most recent call last): ... ValueError: score must between 0 ~ 100!