Python类成员访问权限详解
·
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。 | 在子类或紧急调试时可访问,生产代码中应避免。 | 除非非常必要(如元编程、深度调试),否则不应在类外访问。 |
核心要点:
- Python 的访问控制基于约定而非强制,旨在依靠开发者自觉维护代码封装性 。
- 私有成员通过名称修饰提供了一定保护,但通过修饰后的名称仍可访问,因此并非绝对安全 。
- 合理使用保护成员和私有成员,结合
@property装饰器,可以设计出清晰、健壮且易于维护的类接口 。
参考来源
- Python中的类的成员方法:概念与实现
- python私有成员与公有成员的区别_Python 入门 13 —— 类的公有成员、保护成员 、私有成员...
- Python类的定义和使用
- Python-类变量,成员变量,静态变量,类方法,静态方法,实例方法,普通函数...
- python基础 python类的成员和装饰器
更多推荐
所有评论(0)