Python编程实战 - 面向对象与进阶语法 - 封装与私有属性

在面向对象编程(OOP)中,封装(Encapsulation) 是最核心的概念之一。它强调"数据与方法的绑定",并通过访问控制保护对象的内部状态不被随意修改。简单来说,封装让类更安全、更易维护,也让代码更具可控性和清晰性。


一、什么是封装

封装的核心思想是:

把数据(属性)和行为(方法)组织在一起,对外提供有限的接口访问,而隐藏内部实现细节。

举个例子,假设我们要设计一个银行账户类:

python 复制代码
class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner
        self.balance = balance

    def deposit(self, amount):
        self.balance += amount
        print(f"存入 {amount} 元,当前余额:{self.balance}")

    def withdraw(self, amount):
        if amount > self.balance:
            print("余额不足!")
        else:
            self.balance -= amount
            print(f"取出 {amount} 元,当前余额:{self.balance}")

在这里,BankAccount 将"账户信息"和"操作方法"封装到一个类中,对外只暴露 deposit()withdraw() 两个安全接口,而不让外部直接修改余额。


二、私有属性与私有方法

Python 没有像 Java 那样严格的访问控制符(public、private、protected),但提供了约定俗成的方式来实现封装。

  • 单下划线 _attr:表示受保护的属性,建议只在类或子类中访问。
  • 双下划线 __attr:表示私有属性,会触发"名称重整"(Name Mangling),外部无法直接访问。

例如:

python 复制代码
class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner
        self.__balance = balance  # 私有属性

    def deposit(self, amount):
        self.__balance += amount
        print(f"存入 {amount} 元,当前余额:{self.__balance}")

    def __check_balance(self):  # 私有方法
        print(f"账户余额:{self.__balance}")

    def show_balance(self):
        self.__check_balance()

使用时:

python 复制代码
acc = BankAccount("小明", 1000)
acc.deposit(500)
acc.show_balance()

输出:

复制代码
存入 500 元,当前余额:1500
账户余额:1500

如果你尝试:

python 复制代码
print(acc.__balance)

会报错:

csharp 复制代码
AttributeError: 'BankAccount' object has no attribute '__balance'

三、为什么需要封装

  1. 保护数据安全 防止外部代码直接修改对象内部数据。 例如:外部不能直接把余额改成负数。

  2. 控制访问权限 通过方法控制数据操作逻辑,保证一致性与正确性。

  3. 隐藏实现细节 外部只需调用接口,不关心内部实现。 例如用户不需要知道银行系统如何校验密码,只需要使用登录接口。


四、访问私有属性的方法

虽然双下划线属性外部无法直接访问,但 Python 提供了间接方式:

python 复制代码
print(acc._BankAccount__balance)

输出:

yaml 复制代码
1500

⚠️ 这不是推荐做法,只是说明 Python 的封装是"约定优于强制"。 在实际开发中,应遵守封装原则,通过方法或属性装饰器访问内部数据。


五、通过 getter 和 setter 管理属性

如果希望安全地访问和修改私有属性,可以使用 属性方法(Property)

python 复制代码
class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner
        self.__balance = balance

    @property
    def balance(self):
        return self.__balance

    @balance.setter
    def balance(self, amount):
        if amount < 0:
            print("余额不能为负!")
        else:
            self.__balance = amount

使用方式:

python 复制代码
acc = BankAccount("小红", 1000)
print(acc.balance)  # 调用 getter
acc.balance = 2000  # 调用 setter
print(acc.balance)

输出:

yaml 复制代码
1000
2000

这样,我们既能保持封装性,又能以更自然的方式访问数据。


六、总结

概念 说明
封装 将属性与方法组合并限制外部访问
私有属性 __ 开头,只能在类内部访问
受保护属性 _ 开头,约定仅供类及子类使用
私有方法 __ 开头的方法,只能内部调用
@property 用于优雅地封装属性访问与修改

封装让对象更像"黑箱",使用者只需通过接口操作,而不用关心内部结构。这种设计不仅提高了程序的安全性和可维护性,也让代码更符合"高内聚、低耦合"的设计原则。


相关推荐
nvd117 小时前
python异步编程 -- 理解协程函数和协程对象
开发语言·python
IT_陈寒7 小时前
Spring Boot 3.2性能翻倍!我仅用5个技巧就让接口响应时间从200ms降到50ms
前端·人工智能·后端
风象南7 小时前
Spring Boot 手撸一个自助报表系统
后端
donotshow7 小时前
Spring Boot 整合 ShedLock 处理定时任务重复
java·后端
木土雨成小小测试员7 小时前
简单创建一个flask项目
后端·python·flask
java1234_小锋8 小时前
PyTorch2 Python深度学习 - transform预处理转换模块
开发语言·python·深度学习·pytorch2
Victor3568 小时前
Redis(100)如何防止Redis的数据丢失?
后端
Victor3568 小时前
Redis(101)Redis为什么是单线程的?
后端
程序员三明治9 小时前
选 Redis Stream 还是传统 MQ?队列选型全攻略(适用场景、优缺点与实践建议)
java·redis·后端·缓存·rocketmq·stream·队列