当前位置:首页>python>《看漫画学Python》学习笔记10:类与对象(中)

《看漫画学Python》学习笔记10:类与对象(中)

  • 2026-10-11 07:15:53
《看漫画学Python》学习笔记10:类与对象(中)

封装是面向对象三大特性(封装、继承、多态)之一。它的核心思想是:隐藏对象的内部细节,只保留有限的对外接口,既降低了复杂度,又保护了数据安全。

一、封装基础概念

# 1. 为什么要封装?

- 保护数据:防止外部代码随意修改关键属性(如账户余额、年龄不能为负数)。

- 隔离变化:修改内部实现时,只要公开接口不变,外部代码无需改动。

- 简化使用:外部只需调用简单的方法,不需要了解复杂的内部逻辑。

# 2. 私有变量与私有方法

在Python 中,以双下划线 __ 开头(但不以双下划线结尾)的成员会被视为私有。Python 会对它们进行名称修饰(Name Mangling),将 __name 自动变成 _类名__name,从而阻止外部直接访问。

- 私有类变量:__interest_rate

- 私有实例变量:self.__amount

- 私有实例方法:def __get_info(self):

重要规则:私有成员只能在类的内部访问(包括私有方法内部),外部直接访问会引发AttributeError。

# 3. 访问私有成员的两种方式

- 传统 getter / setter 方法:通过定义公有的 get_xxx() 和 set_xxx() 方法来读写私有属性。优点是可以添加验证逻辑(如检查年龄是否合理)。

- 使用 @property 装饰器:将方法伪装成属性,使访问方式更自然:obj.age 和 obj.age = 新值。既保持了封装性,又提供了与直接访问属性相同的语法。

二、综合案例:银行账户(Account)的封装实现

# 完整代码

python

class Account:

# 私有类变量:银行利率(不可直接从外部修改)

__interest_rate = 0.025   # 2.5%

def __init__(self, owner, amount):

self.owner = owner          # 公有实例变量(户主名)

self.__amount = amount      # 私有实例变量(账户余额)

# 私有实例方法:返回账户信息字符串(外部不能直接调用)

def __get_info(self):

return f"{self.owner} 余额:{self.__amount} 利率:{Account.__interest_rate}"

# ---------- 公有方法:对外接口 ----------

def deposit(self, money):

if money > 0:

self.__amount += money

print(f"{self.owner} 存入 {money} 元,新余额 {self.__amount}")

else:

print("存款金额必须为正数")

def withdraw(self, money):

if 0 < money <= self.__amount:

self.__amount -= money

print(f"{self.owner} 取出 {money} 元,新余额 {self.__amount}")

else:

print("余额不足或金额无效")

def get_amount(self):

return self.__amount

def set_amount(self, amount):

if amount >= 0:

self.__amount = amount

else:

print("余额不能为负数")

@property

def amount(self):

return self.__amount

@amount.setter

def amount(self, amount):

if amount >= 0:

self.__amount = amount

else:

print("余额不能为负数")

def show_info(self):

print(self.__get_info())

# ---------- 测试代码 ----------

acc = Account("张三", 10000)

acc.deposit(2000)

acc.withdraw(500)

print("余额(getter):", acc.get_amount())

acc.set_amount(8000)

print("余额(setter后):", acc.get_amount())

print("余额(property):", acc.amount)

acc.amount = 12000

print("余额(property赋值后):", acc.amount)

acc.show_info()

运行输出:

张三存入2000 元,新余额 12000

张三取出500 元,新余额 11500

余额(getter): 11500

余额(setter后): 8000

余额(property): 8000

余额(property赋值后): 12000

张三余额:12000 利率:0.025

三、案例解析:封装的不同层次

以下列出本案例中各类成员的定义方式、访问规则及具体示例,无表格,便于复制。

- 公有实例变量 

定义:self.owner = owner  

内部访问:self.owner  

外部访问:acc.owner  

本案例示例:owner

- 私有实例变量 

定义:self.__amount = amount  

内部访问:self.__amount  

外部访问:不可直接访问(acc.__amount 会报错)  

本案例示例:__amount

- 私有类变量 

定义:__interest_rate = 0.025  

内部访问:Account.__interest_rate  

外部访问:不可直接访问

本案例示例:__interest_rate

- 私有实例方法 

定义:def __get_info(self):  

内部访问:self.__get_info()  

外部访问:不可直接调用

本案例示例:__get_info

- 传统 getter 方法 

定义:def get_amount(self): return self.__amount  

内部访问:self.get_amount()  

外部访问:acc.get_amount()  

本案例示例:get_amount

- 传统 setter 方法 

定义:def set_amount(self, amount): self.__amount = amount  

内部访问:self.set_amount(8000)  

外部访问:acc.set_amount(8000)  

本案例示例:set_amount

- property getter 

定义:@property def amount(self): return self.__amount  

内部访问:self.amount  

外部访问:acc.amount  

本案例示例:amount(只读)

- property setter 

定义:@amount.setter def amount(self, amount): self.__amount = amount  

内部访问:self.amount = 新值  

外部访问:acc.amount = 新值  

本案例示例:amount 的赋值

- 公有方法(内部调用私有成员) 

定义:def show_info(self): print(self.__get_info())  

内部访问:self.show_info()  

外部访问:acc.show_info()  

本案例示例:show_info

四、常见问题与最佳实践

1. 私有成员真的是“不可访问”吗? 

Python 的私有是通过名称修饰实现的,仍然可以通过 _类名__变量名 访问,但这是一种约定,不应该这样做。正确的封装是信任调用者遵守规则。

2. 何时使用 getter/setter vs @property? 

- 如果只需要简单的读写,且未来可能需要添加逻辑,使用 @property 最优雅。  

- 如果需要在修改时执行复杂验证或触发其他操作,传统 getter/setter 更明显。  

- 如果没有特殊需求,直接使用公有属性也可以,但为了封装性,建议将关键数据设为私有。

3. 为什么还要提供 setter? 

直接暴露私有属性虽然可以赋值,但无法控制值的合法性。setter 可以在修改前进行检查,例如余额不能为负数。

4. 私有方法的作用是什么? 

将内部复杂逻辑隐藏起来,外部只需要调用简单的公有方法。例如__get_info 被 show_info 调用,外部不需要知道内部字符串的格式。

五、总结

- 封装:隐藏内部细节,通过公开接口操作对象。

- 私有成员:以 __ 开头,限制外部直接访问,类内部自由使用。

- 访问私有成员的方式: 

- 传统 getter / setter 方法(明确、可加验证)  

- @property 装饰器(优雅、像属性一样使用)

- 设计原则:对外提供最小必要接口,内部实现可以随时优化而不影响外部代码。

最新文章

随机文章