应用__slots__
一切正常状况下,在我们界定了一个class,建立了一个class的实例后 ,我们可以给该实例关联一切属性和方式,这就是动态语言的协调能力。先界定class:
class Student(object): pass
随后,试着给实例关联一个属性:
>>> s = Student() >>> s.name = 'Michael' # 动态性给实例关联一个属性 >>> print(s.name) Michael
还能够试着给实例关联一个方式:
>>> def set_age(self, age): # 界定一个涵数做为实例方式 ... self.age = age ... >>> from types import MethodType >>> s.set_age = MethodType(set_age, s) # 给实例关联一个方式 >>> s.set_age(25) # 启用实例方式 >>> s.age # 检测結果 25
可是 ,给一个实例关联的方式,对另一个实例是失灵的:
>>> s2 = Student() # 建立新的实例 >>> s2.set_age(25) # 试着启用方式 Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'set_age'
为了更好地给全部实例都关联方式,能够给class关联方式:
>>> def set_score(self, score): ... self.score = score ... >>> Student.set_score = set_score
给class关联方式后 ,全部实例均可启用:
>>> s.set_score(100) >>> s.score 100 >>> s2.set_score(99) >>> s2.score 99
一般状况下,上边的set_score方式能够立即界定在class中,但动态性关联容许我们在程序执行的全过程中动态性给class再加上作用 ,这在静态数据語言中难以完成。
应用__slots__
可是,如果我们要想限定实例的属性该怎么办?例如,只容许对Student实例加上name和age属性 。
为了更好地做到限定的目地 ,Python容许在界定class的情况下 ,界定一个独特的__slots__自变量,来限定该class实例能加上的属性:
class Student(object): __slots__ = ('name', 'age') # 用tuple界定容许关联的属性名字
随后,大家试一下:
>>> s = Student() # 建立新的实例 >>> s.name = 'Michael' # 关联属性'name' >>> s.age = 25 # 关联属性'age' >>> s.score = 99 # 关联属性'score' Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'score'
因为'score'沒有被放进__slots__中 ,因此 不可以关联score属性,尝试关联score将获得AttributeError的不正确。
应用__slots__要留意,__slots__界定的属性仅对当今类实例起功效 ,对承继的子类是失灵的:
>>> class GraduateStudent(Student): ... pass ... >>> g = GraduateStudent() >>> g.score = 9999
除非是在子类中也界定__slots__,那样,子类实例容许界定的属性便是本身的__slots__再加上父类的__slots__。
应用@property
在关联属性时 ,如果我们立即把属性曝露出来,尽管写起來非常简单,可是 ,没法查验主要参数,造成 能够把考试成绩随意改:
s = Student() s.score = 9999
这显而易见漏洞百出 。为了更好地限定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 an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value
如今 ,对随意的Student实例开展实际操作,就不可以无拘无束地设定score了:
>>> s = Student() >>> s.set_score(60) # ok! >>> s.get_score() 60 >>> s.set_score(9999) Traceback (most recent call last): ... ValueError: score must between 0 ~ 100!
可是,上边的启用方式又稍显繁杂 ,沒有立即用属性那么立即简易。
有木有既能查验主要参数,又可以用相近属性那样简易的方法来浏览类的自变量呢?针对完美主义者的Python程序猿而言,它是务必要保证的!
你是否还记得装饰器(decorator)能够给涵数动态性再加上作用吗?针对类的方式 ,装饰器一样起功效。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('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value
@property的完成非常复杂,大家先调查怎么使用 。把一个getter方式变为属性,只必须再加上@property就可以了 ,这时,@property自身又建立了另一个装饰器@score.setter,承担把一个setter方式变为属性取值 ,因此 ,大家就有着一个可控性的属性实际操作:
>>> 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!
注意到这一奇妙的@property ,我们在对实例属性实际操作的情况下,就了解该属性很可能并不是立即曝露的,只是根据getter和setter方式来完成的。
还能够界定写保护属性 ,只界定getter方式,不界定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 2015 - self._birth
上边的birth是可读写能力属性,而age便是一个写保护属性 ,由于age能够依据birth和获取当前时间推算出来。
总结
@property广泛运用在类的界定中,能够让调用者写成简洁明了的编码,另外确保对主要参数开展必需的查验 ,那样,程序执行时就降低了失败的概率 。