Python 中类和实例的成员访问权限主要通过命名约定来实现,而非严格的编译时强制。其核心分为三类:公有成员、保护成员和私有成员 。

一、访问权限分类与命名约定

权限类型 命名约定 访问范围 说明
公有成员 (Public) 普通命名,如 name, method() 类内、类外、子类均可直接访问 默认的成员访问权限 。
保护成员 (Protected) 单下划线开头,如 _name, _method() 类内、子类可访问。类外“不建议”直接访问,但技术上仍可访问。 一种约定俗成的提示,提示开发者“这是一个内部成员,请勿在类外部直接使用” 。
私有成员 (Private) 双下划线开头,如 __name, __method() 仅在定义它的类内部可直接访问。子类和类外部无法直接访问。 Python 通过“名称修饰”机制实现,并非绝对安全,但提供了最强的访问限制 。

二、各类成员详解与代码示例

1. 公有成员

公有成员是最常见的类型,定义和使用没有任何特殊前缀。

class Person:
    # 公有类属性 species = 'Human'

    def __init__(self, name, age):
        # 公有实例属性 self.name = name  # 公有 self.age = age    # 公有 # 公有实例方法
    def introduce(self):
        return f"Hi, I'm {self.name}, {self.age} years old."

# 创建实例
p1 = Person('Alice', 25)

# 类外直接访问公有成员
print(p1.name)               # 输出: Alice 
print(p1.introduce())        # 输出: Hi, I'm Alice, 25 years old. 
print(Person.species)        # 输出: Human 

2. 保护成员

以单下划线 _ 开头,主要是一种编程约定,用于提示该成员仅供内部或子类使用。

class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner        self._balance = balance  # 保护成员,提示不应直接访问 # 保护方法,提示为内部逻辑 def _update_log(self, action):
        print(f"[LOG] Account of {self.owner}: {action}")

    def deposit(self, amount):
        self._balance += amount self._update_log(f"Deposited {amount}")  # 类内部可以访问保护方法

class VIPAccount(BankAccount):
    def check_balance(self):
        # 子类中可以访问父类的保护成员(约定上允许,但不推荐频繁直接操作)
        print(f"Protected balance (from subclass): {self._balance}")

# 使用
acc = BankAccount('Bob', 1000)
acc.deposit(500)  # 输出: [LOG] Account of Bob: Deposited 500

# 类外“可以”但“不应该”直接访问保护成员(仅约定,非强制)
print(acc._balance) # 输出: 1500 

vip = VIPAccount('Charlie', 2000)
vip.check_balance()  # 输出: Protected balance (from subclass): 2000

3. 私有成员

以双下划线 __ 开头,Python 解释器会对其执行名称修饰,使其在类外难以直接访问,从而实现一定程度的私有化。

class SecureData:
    def __init__(self, public_info, secret):
        self.public_info = public_info
        self.__secret = secret  # 私有实例属性
        self._protected_key = 'protected_key'

    def __private_method(self):  # 私有方法
        return "This is a private method."

    def public_interface(self):
        # 公有方法可以访问私有成员
        hidden = self.__private_method()
        return f"Public: {self.public_info}, Secret accessed internally: {self.__secret}. {hidden}"

# 创建实例
data = SecureData('Open Data', 'Top Secret')

# 访问公有成员 正常
print(data.public_info)           # 输出: Open Data
print(data.public_interface())    # 输出: Public: Open Data, Secret accessed internally: Top Secret. This is a private method.

# 尝试直接访问私有成员会引发 AttributeError
# print(data.__secret)            # AttributeError: 'SecureData' object has no attribute '__secret'
# data.__private_method()         # AttributeError

# 通过“名称修饰”后的名称仍可访问(证明非绝对安全)
# 私有属性 `__secret` 被修饰为 `_SecureData__secret`
print(data._SecureData__secret)   # 输出: Top Secret 
# 私有方法 `__private_method` 被修饰为 `_SecureData__private_method`
print(data._SecureData__private_method()) # 输出: This is a private method.

4. 特殊成员:__xxx__

以双下划线开头和结尾的成员是 Python 的特殊方法或属性(如 __init__, __str__),它们是公有接口,用于运算符重载等,不应被视为私有成员 。

三、类方法、静态方法与实例方法的访问权限方法的访问权限规则与属性一致,也遵循单下划线和双下划线的约定。

class MyClass:
    class_attr = "Class Attribute"

    def __init__(self, value):
        self.__private_value = value
        self._protected_value = value * 2 # 公有实例方法 - 可访问实例的所有属性(公有、保护、私有)和类属性
    def instance_method(self):
        return f"Instance: private={self.__private_value}, protected={self._protected_value}, class={self.class_attr}"

    # 保护实例方法
    def _protected_method(self):
        return "This is a protected method."

    # 私有实例方法
    def __private_instance_method(self):
        return "This is a private instance method."

    # 类方法 - 接收类本身作为第一个参数,可访问和修改类属性
    @classmethod def class_method(cls):
        cls.class_attr = "Modified by Class Method"
        return f"Class method accessing {cls.class_attr}"

    # 静态方法 - 与类和实例无绑定,不能直接访问实例或类属性(除非通过参数传入)
    @staticmethod def static_method():
        return "This is a static method."

# 访问示例
obj = MyClass(10)
print(obj.instance_method())       # 输出: Instance: private=10, protected=20, class=Class Attribute 
print(MyClass.class_method())      # 输出: Class method accessing Modified by Class Method 
print(MyClass.static_method())     # 输出: This is a static method. 

# 尝试访问受保护和私有的方法
print(obj._protected_method())     # 输出: This is a protected method. (约定上不推荐)
# print(obj.__private_instance_method()) # AttributeError

四、属性装饰器 @property 与访问控制

@property 装饰器常用于将方法“伪装”成属性,并实现对私有属性的安全访问和验证。

class Temperature:
    def __init__(self, celsius):
        self.__celsius = celsius  # 私有属性,存储实际数据 @property def celsius(self):
        """Getter for celsius (公有读接口)"""
        return self.__celsius

    @celsius.setter def celsius(self, value):
        """Setter for celsius with validation (公有写接口)"""
        if value < -273.15:
            raise ValueError("Temperature below absolute zero is not possible.")
        self.__celsius = value

    @property
    def fahrenheit(self):
        """只读属性,根据 celsius 计算"""
        return self.__celsius * 9 / 5 + 32

# 使用
temp = Temperature(25)
print(temp.celsius)        # 通过getter读取,输出: 25 
temp.celsius = 30          # 通过setter设置
print(temp.celsius)        # 输出: 30
print(temp.fahrenheit)     # 输出: 86.0 (只读,无法直接设置)

# temp.celsius = -300      # 触发 ValueError
# temp.fahrenheit = 100    # AttributeError: can't set attribute
# print(temp.__celsius)    # AttributeError (私有属性无法直接访问)

五、总结与对比

下表从不同维度对比了三种访问权限:

特性 公有成员 保护成员 私有成员
定义方式 name, method() _name, _method() __name, __method()
访问范围 类内、子类、类外任意访问 类内、子类可访问。类外可访问但不建议。 仅在定义它的类内部直接访问。
名称修饰 有(被重命名为 _ClassName__name
设计意图 公开接口,供外部自由调用。 提示为内部实现细节,子类可继承使用,外部慎用。 强内部实现细节,防止子类意外重写或外部直接访问。
访问建议 安全访问,是类的主要API。 在子类或紧急调试时可访问,生产代码中应避免。 除非非常必要(如元编程、深度调试),否则不应在类外访问。

核心要点

  1. Python 的访问控制基于约定而非强制,旨在依靠开发者自觉维护代码封装性 。
  2. 私有成员通过名称修饰提供了一定保护,但通过修饰后的名称仍可访问,因此并非绝对安全 。
  3. 合理使用保护成员和私有成员,结合 @property 装饰器,可以设计出清晰、健壮且易于维护的类接口 。

参考来源

 

更多推荐