属性

属性

访问限制

在某些情况下,我们希望限制用户访问对象的属性或方法,也就是希望它是私有的,对外隐蔽。比如,对于上面的例子,我们希望 name 属性在外部不能被访问,我们可以在属性或方法的名称前面加上两个下划线,即 __,对上面的例子做一点改动:

class Animal(object):
    def __init__(self, name):
        self.__name = name
    def greet(self):
        print 'Hello, I am %s.' % self.__name

>>> dog1 = Animal('dog1')
>>> dog1.__name   # 访问不了
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-206-7f6730db631e> in <module>()
----> 1 dog1.__name

AttributeError: 'Animal' object has no attribute '__name'
>>> dog1.greet()   # 可以访问
Hello, I am dog1.

slots 魔法

在 Python 中,我们在定义类的时候可以定义属性和方法。当我们创建了一个类的实例后,我们还可以给该实例绑定任意新的属性和方法。

class Point(object):
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

>>> p = Point(3, 4)
>>> p.z = 5    # 绑定了一个新的属性
>>> p.z
5
>>> p.__dict__
{'x': 3, 'y': 4, 'z': 5}

在上面,我们创建了实例 p 之后,给它绑定了一个新的属性 z,这种动态绑定的功能虽然很有用,但它的代价是消耗了更多的内存。因此,为了不浪费内存,可以使用 __slots__ 来告诉 Python 只给一个固定集合的属性分配空间,对上面的代码做一点改进,如下:

class Point(object):
    __slots__ = ('x', 'y')       # 只允许使用 x 和 y

    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

上面,我们给 __slots__ 设置了一个元组,来限制类能添加的属性。现在,如果我们想绑定一个新的属性,比如 z,就会出错了,如下:

>>> p = Point(3, 4)
>>> p.z = 5
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-648-625ed954d865> in <module>()
----> 1 p.z = 5

AttributeError: 'Point' object has no attribute 'z'

使用 __slots__ 有一点需要注意的是,__slots__ 设置的属性仅对当前类有效,对继承的子类不起效,除非子类也定义了 __slots__,这样,子类允许定义的属性就是自身的 slots 加上父类的 slots。

使用 @property

在使用 @property 之前,让我们先来看一个简单的例子:

class Exam(object):
    def __init__(self, score):
        self._score = score

    def get_score(self):
        return self._score

    def set_score(self, val):
        if val < 0:
            self._score = 0
        elif val > 100:
            self._score = 100
        else:
            self._score = val

>>> e = Exam(60)
>>> e.get_score()
60
>>> e.set_score(70)
>>> e.get_score()
70

在上面,我们定义了一个 Exam 类,为了避免直接对 _score 属性操作,我们提供了 get_score 和 set_score 方法,这样起到了封装的作用,把一些不想对外公开的属性隐蔽起来,而只是提供方法给用户操作,在方法里面,我们可以检查参数的合理性等。这样做没什么问题,但是我们有更简单的方式来做这件事,Python 提供了 property 装饰器,被装饰的方法,我们可以将其『当作』属性来用,看下面的例子:

class Exam(object):
    def __init__(self, score):
        self._score = score

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, val):
        if val < 0:
            self._score = 0
        elif val > 100:
            self._score = 100
        else:
            self._score = val

>>> e = Exam(60)
>>> e.score
60
>>> e.score = 90
>>> e.score
90
>>> e.score = 200
>>> e.score
100

在上面,我们给方法 score 加上了 @property,于是我们可以把 score 当成一个属性来用,此时,又会创建一个新的装饰器 score.setter,它可以把被装饰的方法变成属性来赋值。另外,我们也不一定要使用 score.setter 这个装饰器,这时 score 就变成一个只读属性了:

class Exam(object):
    def __init__(self, score):
        self._score = score

    @property
    def score(self):
        return self._score

>>> e = Exam(60)
>>> e.score
60
>>> e.score = 200  # score 是只读属性,不能设置值
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-676-b0515304f6e0> in <module>()
----> 1 e.score = 200

AttributeError: can't set attribute
上一页