Python面向对象:封装之私有属性与私有方法

一、开篇:Python的"私有"——一种约定而非强制
封装是OOP的核心原则:隐藏内部实现细节,只暴露必要的接口。但Python的哲学是"我们都是成年人了"——没有Java/C++那种真正的private关键字。Python通过命名约定和名称改编来实现访问控制。
⌨️ 三种访问级别:
class MyClass:
def __init__(self):
self.public = "公开属性" # 公开——谁都可以访问
self._protected = "受保护属性" # 约定——请不要直接访问
self.__private = "私有属性" # 名称改编——较难直接访问(但不是不可能!)
obj = MyClass()
print(obj.public) # 公开属性 ✓
print(obj._protected) # 受保护属性 ✓(但还是能访问)
# print(obj.__private) # AttributeError! —— 不能直接访问
print(obj._MyClass__private) # 私有属性 ✓ —— 知道了改编规则就能访问!
二、下划线命名约定
2.1 单下划线前缀 _var
# _var:约定——"这个是内部实现,请不要直接访问"
# 只是一个约定,Python不会阻止你访问
class BankAccount:
def __init__(self, owner, initial_balance):
self.owner = owner
self._balance = initial_balance # 约定:内部属性
def deposit(self, amount):
"""公开接口——通过方法来操作_balance"""
if amount > 0:
self._balance += amount
def withdraw(self, amount):
"""公开接口"""
if 0 < amount <= self._balance:
self._balance -= amount
return True
return False
def get_balance(self):
"""公开的只读接口"""
return self._balance
acc = BankAccount("张三", 1000)
# ✅ 推荐:通过公开方法访问
acc.deposit(500)
print(acc.get_balance()) # 1500
# ⚠️ 不推荐但可以:直接访问"受保护"属性
print(acc._balance) # 1500 —— Python不会阻止你
acc._balance = 999999 # 可以修改——但破坏了封装!
# 💡 _var在from module import *时不会被导入
# 这是单下划线的另一个作用
# _前缀常用于:
# - 内部实现细节
# - 不希望外部直接修改的属性
# - 辅助方法(_helper_method())
2.2 双下划线前缀 __var(名称改编)
# __var:触发名称改编(Name Mangling)
# Python自动将__var重命名为_ClassName__var
# 目的:防止子类意外覆盖,而不是"真私有"
class Parent:
def __init__(self):
self.__secret = "父类的秘密" # 实际存储为 _Parent__secret
def reveal(self):
return self.__secret # 方法内部:Python自动映射
class Child(Parent):
def __init__(self):
super().__init__()
self.__secret = "子类的秘密" # 实际存储为 _Child__secret
# 不会覆盖父类的__secret!
def child_reveal(self):
return self.__secret
def parent_reveal_via_super(self):
return super().reveal()
c = Child()
print(c.child_reveal()) # 子类的秘密
print(c.parent_reveal_via_super()) # 父类的秘密 —— 两个__secret是独立的!
# 查看实际的属性名
print(c.__dict__)
# {'_Parent__secret': '父类的秘密', '_Child__secret': '子类的秘密'}
# 仍然可以"破解"——如果你知道改编规则
print(c._Parent__secret) # 父类的秘密
print(c._Child__secret) # 子类的秘密
2.3 双下划线前后都有 var
# __var__:Python的特殊方法/魔法方法
# 这是Python自己使用的,不要自定义这种命名
class MyClass:
def __init__(self): # ✅ Python定义的特殊方法
pass
def __str__(self): # ✅ Python定义的特殊方法
return "MyClass实例"
def __len__(self): # ✅ Python定义的特殊方法
return 42
# def __my_method__(self): # ❌ 不要自己定义这种名字!
# pass # 可能与未来Python版本冲突
三、封装的实际应用
3.1 数据验证和访问控制
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("温度不能低于绝对零度(-273.15°C)")
self._celsius = value
@property
def fahrenheit(self):
"""华氏度——只读计算属性"""
return self._celsius * 9/5 + 32
@property
def kelvin(self):
"""开尔文——只读计算属性"""
return self._celsius + 273.15
# 使用
t = Temperature(25)
print(f"{t.celsius}°C = {t.fahrenheit}°F = {t.kelvin}K")
t.celsius = 100
print(f"{t.celsius}°C = {t.fahrenheit}°F")
# t.celsius = -300 # ValueError: 温度不能低于绝对零度
3.2 隐藏复杂实现
import hashlib
import os
class User:
"""用户类——隐藏密码的存储和验证细节"""
def __init__(self, username, password):
self.username = username
self.__salt = os.urandom(16).hex() # 私有:盐
self.__password_hash = self.__hash_password(password) # 私有:密码哈希
def __hash_password(self, password):
"""私有方法:对密码做哈希——外部不需要知道怎么做的"""
data = (self.__salt + password).encode()
return hashlib.sha256(data).hexdigest()
def check_password(self, password):
"""公开方法:验证密码"""
return self.__hash_password(password) == self.__password_hash
def change_password(self, old_password, new_password):
"""公开方法:修改密码"""
if not self.check_password(old_password):
raise ValueError("原密码不正确")
self.__password_hash = self.__hash_password(new_password)
# 使用
user = User("zhangsan", "secure123")
print(user.check_password("secure123")) # True
print(user.check_password("wrong")) # False
# 这些对外部隐藏:
# user.__salt # AttributeError
# user.__password_hash # AttributeError
# user.__hash_password() # AttributeError
四、Python封装的最佳实践
# ✅ Python封装原则:
# 1. 公开属性——直接用
# 适合:简单的数据,无需验证
class Point:
def __init__(self, x, y):
self.x = x # 公开——用户可以直接访问和修改
self.y = y
# 2. _protected属性——约定不要直接访问
# 适合:内部实现细节,但子类可能需要访问
class BaseModel:
def __init__(self):
self._data = {} # 子类可能需要
self._cache = {} # 内部缓存
# 3. __private属性——防止子类覆盖
# 适合:框架/库中,确保绝对不被覆盖
class Framework:
def __init__(self):
self.__internal_counter = 0 # 防止用户子类意外覆盖
# 4. @property——控制访问
# 适合:需要验证或计算的属性
class Person:
def __init__(self, age):
self._age = age
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if value < 0:
raise ValueError("年龄不能为负")
self._age = value
五、总结
Python的封装哲学是"我们都是成年人了"——通过命名约定而非强制访问控制来实现封装。_var是约定,__var是名称改编。
💡 核心规则:
| 命名 | 含义 | 使用场景 |
|---|---|---|
var | 公开 | 普通属性 |
_var | “受保护”(约定) | 内部实现、from import *不导出 |
__var | 名称改编 | 防止子类意外覆盖 |
__var__ | 特殊方法 | Python自己用,不要自定义 |
✅ Python式封装:用_前缀标记"内部"属性,用@property提供受控访问,只在框架/库中用__防止子类覆盖。

3178

被折叠的 条评论
为什么被折叠?



