访问限制
1. 为什么需要访问限制
- 类内部有属性和方法,外部可通过实例变量操作数据;若属性直接暴露,外部可随意修改(如
bart.score = 99),内部逻辑无法保护。 - 访问限制:把不希望被外部直接改动的属性设为“私有”,通过方法读写,便于封装和参数校验,代码更健壮。
2. 私有变量:双下划线 __ 开头
- 实例变量名以 两个下划线
__开头 → 视为私有变量(private),仅类内部可访问,外部不能直接访问。
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
- 外部访问
bart.__name会报错:AttributeError: 'Student' object has no attribute '__name'。
3. 对外提供 getter / setter
- 读取:提供
get_name()、get_score()等方法返回私有属性。 - 修改:提供
set_score(score)等方法;在方法内可校验参数(如分数必 须在 0~100),避免无效数据。
def get_name(self):
return self.__name
def get_score(self):
return self.__score
def set_score(self, score):
if 0 <= score <= 100:
self.__score = score
else:
raise ValueError('bad score')
- 相比直接
bart.score = 99,通过 setter 可以在一处统一校验,更安全。
4. 命名约定与特殊变量
| 写法 | 含义 | 是否“私有” |
|---|---|---|
__xxx__ | 特殊变量(前后双下划线),如 __name__、__init__ | 可直接访问,不要用作私有变量名 |
__xxx | 私有变量(仅前双下划线) | 类内私有,外部不直接访问 |
_xxx | 单下划线开头 | 约定“视为私有”,外部可访问,但不建议随意访问 |
- 不要用
__name__、__score__这种前后双下划线当作私有属性名,会与 Python 内置特殊变量冲突。
5. 双下划线的实现(了解即可)
- 解释器会把
__name改写为_类名__name(如_Student__name),所以理论上可通过bart._Student__name访问。 - 强烈不建议依赖这种写法:不同版本解释器可能改名方式不同;应视为实现细节,遵守“不直接访问私有”的约定。
6. 常见错误:外部给实例加 __name
bart.__name = 'New Name' # 表面“成功”
bart.__name # 'New Name'
bart.get_name() # 仍是 'Bart Simpson'
- 类内部的
__name已被改成_Student__name;bart.__name = 'New Name'是给实例新增了一个属性__name,与类内部的私有变量不是同一个。 - 正确做法:通过
set_xxx()或提供的接口修改,不要在外边直接写实例.__属性。
7. 小结
- 私有:属性名以
__开头(且不要用__xxx__),通过 getter/setter 访问与校验。 - 约定:
_xxx视为“请勿随意访问”;Python 没有真正的访问强制,全靠约定和自觉。
练习
将 Student 的 gender 对外隐藏,用 get_gender() 和 set_gender() 代替,并做参数有效性检查(如只允许 'male' / 'female')。
class Student(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
# 测试
bart = Student('Bart', 'male')
if bart.get_gender() != 'male':
print('测试失败!')
else:
bart.set_gender('female')
if bart.get_gender() != 'female':
print('测试失败!')
else:
print('测试成功!')