访问限制

Python 类属性封装与访问控制教程

1. 封装的概念

封装是面向对象编程的三大特性之一(封装、继承、多态),它指的是将数据和操作数据的方法绑定在一起,并对外部隐藏内部实现的细节。

在Python中,封装主要通过命名约定来实现对类成员的访问控制。

2. 访问控制级别

2.1 公共成员 (Public)

默认情况下,Python类的所有属性和方法都是公共的,可以在类外部直接访问:

class Student:
    def __init__(self, name):
        self.name = name  # 公共属性

s = Student("Alice")
print(s.name)  # 可以直接访问

2.2 受保护成员 (Protected)

以一个下划线 _ 开头的成员被认为是受保护的,按照约定不应该在类外部直接访问,但Python并不强制限制:

class Student:
    def __init__(self, name):
        self._name = name  # 受保护属性

s = Student("Alice")
print(s._name)  # 仍然可以访问,但不建议

2.3 私有成员 (Private)

以双下划线 __ 开头的成员被认为是私有的,Python会进行名称改写(name mangling)来限制直接访问:

class Student:
    def __init__(self, name):
        self.__name = name  # 私有属性

s = Student("Alice")
# print(s.__name)  # 报错: AttributeError

3. 实现真正的封装

3.1 使用属性访问器

为了实现对私有属性的安全访问,通常使用getter和setter方法:

class Student:
    def __init__(self, name, score):
        self.__name = name
        self.__score = score
    
    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("分数必须在0-100之间")

3.2 使用@property装饰器 (推荐)

Python提供了更优雅的属性访问方式:

class Student:
    def __init__(self, name, score):
        self.__name = name
        self.__score = score
    
    @property
    def name(self):
        return self.__name
    
    @property
    def score(self):
        return self.__score
    
    @score.setter
    def score(self, value):
        if 0 <= value <= 100:
            self.__score = value
        else:
            raise ValueError("分数必须在0-100之间")

使用方式:

s = Student("Bob", 85)
print(s.name)  # 像访问属性一样调用getter
s.score = 90   # 像赋值一样调用setter

4. 名称改写机制

Python对私有成员使用名称改写(name mangling)机制,将 __var 改为 _ClassName__var

class Student:
    def __init__(self, name):
        self.__name = name

s = Student("Alice")
print(dir(s))  # 可以看到 '_Student__name'
print(s._Student__name)  # 可以这样访问,但不推荐

5. 特殊变量名

以双下划线开头和结尾的变量(如 __init__)是Python的特殊方法/属性,不会被名称改写:

class Student:
    def __init__(self):
        self.__special__ = "This is special"

s = Student()
print(s.__special__)  # 可以直接访问

6. 实践练习

按照要求修改Student类,封装gender属性:

class Student:
    def __init__(self, name, gender):
        self.__name = name
        self.__gender = gender
    
    def get_gender(self):
        return self.__gender
    
    def set_gender(self, gender):
        if gender in ('male', 'female'):
            self.__gender = gender
        else:
            raise ValueError("性别必须是'male'或'female'")

# 测试代码
bart = Student('Bart', 'male')
if bart.get_gender() != 'male':
    print('测试失败!')
else:
    bart.set_gender('female')
    if bart.get_gender() != 'female':
        print('测试失败!')
    else:
        print('测试成功!')

7. 最佳实践

  1. 除非有特殊需要,属性应该设为私有(__开头)
  2. 使用@property装饰器实现属性访问控制
  3. 在setter方法中添加参数验证逻辑
  4. 避免直接访问 _ClassName__var 这样的改写后名称
  5. 单下划线 _var 表示受保护成员,仅作为约定

通过良好的封装实践,可以构建更健壮、更安全的面向对象程序。