属性
属性
访问限制
在某些情况下,我们希望限制用户访问对象的属性或方法,也就是希望它是私有的,对外隐蔽。比如,对于上面的例子,我们希望 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