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

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提供受控访问,只在框架/库中用__防止子类覆盖。

评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值