实例属性和类属性

 

实例属性通过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!
posted @ 2016-07-30 23:59  黄小墨  阅读(2304)  评论(0编辑  收藏  举报