在面向对象编程的三大支柱中,封装(Encapsulation) 是最基础也最重要的概念之一。昨天我们学习了继承与多态,今天来深入理解封装的核心思想及其在 Python 中的实现方式。
什么是封装?
封装是将数据(属性)和操作数据的方法绑定在一起,并对外隐藏内部实现细节的机制。简单来说:
封装 = 数据 + 方法 + 访问控制
封装的好处:
- 🔒 安全性:防止外部代码随意修改内部状态
- 🧩 模块化:每个类都是独立的黑盒,便于维护
- 🔄 灵活性:内部实现可以改变,不影响外部调用
Python 中的访问控制
Python 不像 Java 那样有严格的 public、private、protected 关键字,而是通过命名约定来实现访问控制:
| 前缀 | 含义 | 访问级别 |
|---|---|---|
name | 普通属性 | 公开(public) |
_name | 单下划线 | 受保护(protected),约定不要外部访问 |
__name | 双下划线 | 私有(private),名称改写 |
示例:访问控制演示
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner # 公开属性
self._account_type = "Savings" # 受保护属性
self.__balance = balance # 私有属性
def deposit(self, amount):
"""公开方法:存款"""
if amount > 0:
self.__balance += amount
return True
return False
def withdraw(self, amount):
"""公开方法:取款"""
if 0 < amount <= self.__balance:
self.__balance -= amount
return True
return False
def get_balance(self):
"""公开方法:查询余额"""
return self.__balance
def _calculate_interest(self):
"""受保护方法:计算利息"""
return self.__balance * 0.03
# 使用示例
account = BankAccount("Alice", 1000)
# ✅ 可以访问公开属性
print(f"账户持有人:{account.owner}")
# ✅ 可以调用公开方法
account.deposit(500)
print(f"当前余额:{account.get_balance()}")
# ⚠️ 可以访问但不应该(约定)
print(f"账户类型:{account._account_type}")
# ❌ 无法直接访问私有属性(会报错)
# print(account.__balance) # AttributeError!
# 🔓 但可以通过名称改写访问(不推荐)
print(account._BankAccount__balance)
使用 @property 实现优雅的封装
Python 提供了 @property 装饰器,让我们能够以更优雅的方式控制属性访问:
class Temperature:
def __init__(self, celsius=0):
self._celsius = celsius
@property
def celsius(self):
"""获取摄氏温度"""
return self._celsius
@celsius.setter
def celsius(self, value):
"""设置摄氏温度(带验证)"""
if value < -273.15:
raise ValueError("温度不能低于绝对零度!")
self._celsius = value
@property
def fahrenheit(self):
"""获取华氏温度(只读计算属性)"""
return self._celsius * 9/5 + 32
@fahrenheit.setter
def fahrenheit(self, value):
"""设置华氏温度(反向计算)"""
self._celsius = (value - 32) * 5/9
# 使用示例
temp = Temperature(25)
# ✅ 像访问属性一样使用
print(f"摄氏:{temp.celsius}°C")
print(f"华氏:{temp.fahrenheit}°F")
# ✅ 设置时自动验证
temp.celsius = 30 # OK
# temp.celsius = -300 # ValueError!
# ✅ 可以通过华氏设置
temp.fahrenheit = 100
print(f"设置为 100°F 后,摄氏:{temp.celsius:.1f}°C")
实战:设计一个安全的用户类
让我们综合运用封装技巧,设计一个完整的用户管理类:
import hashlib
from datetime import datetime
class User:
"""用户类 - 展示完整的封装实践"""
def __init__(self, username, email, password):
self.username = username
self.email = email
self.__password_hash = self._hash_password(password)
self.__created_at = datetime.now()
self.__login_count = 0
self.__is_active = True
@staticmethod
def _hash_password(password):
"""私有静态方法:密码哈希"""
return hashlib.sha256(password.encode()).hexdigest()
def verify_password(self, password):
"""验证密码"""
if not self.__is_active:
return False
return self._hash_password(password) == self.__password_hash
def login(self, password):
"""登录方法"""
if self.verify_password(password):
self.__login_count += 1
return True
return False
@property
def created_at(self):
"""只读属性:创建时间"""
return self.__created_at.strftime("%Y-%m-%d %H:%M:%S")
@property
def login_count(self):
"""只读属性:登录次数"""
return self.__login_count
def deactivate(self):
"""停用账户"""
self.__is_active = False
def __str__(self):
return f"User({self.username}, {self.email})"
# 使用示例
user = User("alice", "alice@example.com", "secret123")
# ✅ 正常登录
print(user.login("secret123")) # True
print(f"登录次数:{user.login_count}")
# ✅ 查看公开信息
print(f"用户:{user.username}")
print(f"创建时间:{user.created_at}")
# ❌ 无法直接修改敏感数据
# user.__password_hash = "hacked" # AttributeError!
# user.__login_count = 999 # AttributeError!
# ✅ 通过公开方法安全操作
user.deactivate()
print(user.login("secret123")) # False (已停用)
封装的最佳实践
- 默认私有:除非明确需要公开,否则将属性设为私有
- 提供受控访问:通过
@property或方法提供安全的访问接口 - 验证输入:在 setter 中验证数据的有效性
- 隐藏实现细节:外部不需要知道内部如何存储或计算
- 保持接口稳定:即使内部改变,公开接口应保持不变