第一部分:Python 函数进阶知识点
01. 参数的传递方式
1.1 分析代码执行结果
观察下面的代码,说明位置 1 和位置 2 的输出结果,并解释为什么?
-
位置 1:800 ["hello"]
-
位置 2:800 ["hello", "world"]
-
原因:整数是不可变数据、列表是可变数据;本质是内存地址传递导致的差异
声明两个全局变量
a = 800
b = ["hello"]声明一个函数
def fn(x, y):
"""通过x,y修改数据"""
x = 1000
y.append("world")print(a, b) # 位置1
调用函数修改 数据
fn(a, b)
print(a, b) # 位置2
1.2 内存参数传递模型
① 声明变量,内存申请空间,将内存地址赋值给变量
声明了 a, b 变量,内存中会申请空间,分别存储对应的数据,并且将存储数据的内存地址分别赋值给 a, b
② 函数传参:实际参数把内存地址传递给形式参数
调用函数 fn () 时,通过函数调用语法,将 a, b 实际参数的数据,传递给 x, y 形式参数,内存中是将 a, b 的内存地址传递给了形式参数 x, y;所以可以通过 x 访问 a 的数据,可以通过 y 访问 b 的数据
③ 修改变量:
-
不可变类型(int/str/tuple):重新赋值 = 新地址,不影响外部
-
可变类型(list/dict):修改内部数据 = 直接改原地址内容,影响外部
函数内部通过变量x, y修改数据时,内存中发生了如下的操作
小总结
-
函数参数传递本质:传递内存地址(引用传递)
-
可变类型参数:函数内修改会影响外部
-
不可变类型参数:函数内修改不影响外部

1.3 总结 - 关于值 / 引用传递
# 声明两个全局变量
a = 800
b = ["hello"]
def fn(x, y):
x = 1000
y = ["hello", "world"] # 重新赋值新列表
print(a, b) # 位置1:800 ["hello"]
fn(a, b)
print(a, b) # 位置2:800 ["hello"]
重要结论
-
Python 中只有引用传递,没有值传递
-
对形参整体重新赋值,无论可变 / 不可变,都不会影响外部实参
02. 函数递归
2.1 认识递归
-
递归:函数内部调用自身
-
必须有终止条件,否则无限递归崩溃
-
适合:有重复子问题的逻辑(阶乘、斐波那契)
基本语法
def 函数():
# 函数内部包含多行代码
# 内部调用了自己 - 调用过程 - 递归
函数()
递归特点:
函数封装了多行代码,表示了一个业务的执行流程
函数内部调用自身,就是一种业务流程的重复执行,相当于循环执行,是一整套 / 整个函数 代码复用!
用户名校验案例
def reg_username():
username = input("请输入账号(长度6~12):")
if not 6 <= len(username) <= 12:
input("账号长度不合法,按任意键重新输入")
return reg_username() # 递归
print("合法账号:", username)
reg_username()
2.2 递归经典案例
递归的好处
递归,能让重复的业务流程,编写代码的时候更加容易理解和编写
(1)斐波那契数列
def fib(n):
if n == 1 or n == 2:
return 1
return fib(n-1) + fib(n-2)
(2)阶乘计算
def fac(n):
if n == 1:
return 1
return n * fac(n-1)
2.3 递归总结
递归 (Recursion) 是一个非常高级的语法,让包含一定规则的业务逻辑,编写代码的时候更加容易理解和编码;但是递归的底层执行逻辑决定了它会大量消耗内存空间,所以在脚本编写 / 应用开发的时候一定要慎重使用
常规规范:单层递归 (函数内部只调用自己一次),递归次数不建议超过 10 次
项目组规范:很多项目组为了避免递归造成的问题,直接规范中要求禁止使用递归 (造成的问题很难查询)!
Python 中,可以通过编码实现对递归层次的控制
注意:系统中可以设置递归次数,但是一般很少修改
原因:限制递归次数是为了显示开发人员;Python 内建模块中也使用了大量的递归,如果修改为 10 次递归次数的话,编写的大量脚本 (包含了其他模块) 都会报错!
>>> import sys
>>> sys.getrecursionlimit()
1000
>>>
>>>
>>> sys.setrecursionlimit(10)
>>> sys.getrecursionlimit()
10
03. 函数闭包
3.1、 问题分析
需求:某公司员工小张,开发了自己的脚本库,但是最近脚本运行出现了一些问题,需要对脚本中的函数进行监控,记录函数执行的状态、记录函数执行的时间;
# 小张脚本
def serv_01():
# 记录开始状态、开始时间
print("serv_01处理功能")
# 记录结束状态,结束时间
# 计算总耗时
def serv_02():
print("serv_02处理功能")
def serv_03():
print("serv_03处理功能")
def serv_04():
print("serv_04处理功能")
serv_01()
serv_02()
serv_03()
serv_04()
3.2、 解决方案
常规解决方案,就是逐个函数进行编码
-
事情紧急的情况下,没有更好的方案,手工操作也是一种折中的方案!先动起来!
小张脚本
import time
def serv_01():
start = time.time()
print("记录开始状态")
print("serv_01处理功能")
time.sleep(0.5)
print("记录结束状态")
print(f"总耗时:{time.time() - start}ms")def serv_02():
print("serv_02处理功能")def serv_03():
print("serv_03处理功能")def serv_04():
print("serv_04处理功能")serv_01()
serv_02()
serv_03()
serv_04()
3.3 认识闭包
闭包 = 外部函数 + 内部函数 + 内部使用外部变量
-
延长局部变量生命周期
-
不修改原函数,实现功能扩展
基本语法
def outer():
"""外部函数"""
msg = "外部函数局部变量"
def inner():
"""内部函数:闭包函数"""
print("msg:", msg)
# 返回内部函数的引用
return inner
闭包特点
-
函数内部声明的函数
-
内部函数使用了外部函数的局部变量
-
外部函数最后返回了内部函数的引用
闭包作用
-
延长局部变量作用域,让局部变量不再局限于一个函数内部
-
局部变量:局部变量只能在当前函数内部访问,当前函数执行完成,局部变量立即销毁
-
闭包 - 局部变量在函数执行期间被保留,可以在函数外部访问局部变量
-
-
闭包可以在不修改目标函数的情况下,给函数添加新的功能
- 闭包的一种特殊语法:称为装饰器
3.4 闭包应用:装饰器
装饰器 = 闭包的经典应用,无侵入式增强函数
装饰器语法
def 装饰器名称(fn):
"""参数fn,是一个函数类型,表示目标函数"""
def wrapper():
print("目标函数执行之间添加的功能")
# 执行目标函数
fn()
print("目标函数执行之后添加的功能")
return wrapper
@装饰器名称
def serv_01():
print("执行serv_01功能")
serv_01()
装饰器应用
import time
def timer(fn):
"""给目标函数计时的装饰器"""
def wrapper():
# 记录开始时间
start = time.time()
# 执行目标函数
fn()
# 记录结束时间
end = time.time()
print(f"总耗时:{end -start}ms")
return wrapper
def status(fn):
"""记录状态装饰器"""
def wrapper():
print("开始执行")
fn()
print("执行完毕")
return wrapper
import time
# 小张脚本
# serv_01函数,添加了计时、记录状态功能
@status
@timer
def serv_01():
time.sleep(0.5)
print("serv_01处理功能")
# serv_02函数,添加了计时功能
@timer
def serv_02():
print("serv_02处理功能")
# serv_03函数,添加了记录状态的功能
@status
def serv_03():
print("serv_03处理功能")
def serv_04():
print("serv_04处理功能")
serv_01()
print("-------------------------")
serv_02()
print("-------------------------")
serv_03()
print("-------------------------")
serv_04()
3.5、 闭包的另一个作用
小红打车,3 公里内 8 元;超过 3 公里每公里 2 元,如果停车等待每分钟 1.5 元;
下面是小红的行程:行驶了 5 公里、等 1 分钟红灯、行驶 2 公里、堵车等待 8 分钟;继续行驶 4 公里到达目的地;
① 使用传统语法,编写代码实现,通过全局变量实现
total = 0 # 总价格
def texi(k=0, t=0):
"""计费价格,k公里数,t分钟数"""
global total
# 此处编写代码.....
texi(k=5)
texi(t=1)
texi(k=2)
texi(t=8)
texi(k=4)
print("总费用:", total)
② 使用闭包语法,通过局部变量记录总费用 (闭包 - 延伸了局部变量作用范围)
def texi(k=0, t=0):
"""计费价格,k公里数,t分钟数"""
total = 0 # 记录总费用
# 此处编写代码
...
c = texi()
c(k=5)
c(t=1)
c(k=2)
c(t=8)
money = c(k=4)
print("总费用:", money)
04. 匿名函数(lambda 表达式)
4.1、 认识匿名函数
匿名函数:就是表示一种没有名称的函数,在大部分的编程语言中都有对应的实现
- 如:JavaScript 语法,提供了一种箭头函数的语法,就是一种匿名函数
javascript
运行
// 匿名函数,也称为箭头函数
let add = (x, y) => x + y
// 等价于
function add(a, b){
return a + b
}
Python 中也提供了一种匿名函数的实现,主要通过lambda 关键字声明的表达式 (简称为 lambda 表达式)
python
运行
# 1. 普通函数
def addition(a, b):
return a + b
# 2. 匿名函数:等价与前面的普通函数
# lambda表达式
addition2 = lambda x, y : x + y
print(addition(11, 22))
print(addition2(11, 22))
4.2、 lambda 表达式
基础语法:
python
运行
存储表达式的变量 = lambda 参数列表: 返回一个数据的表达式
代码示例:
python
运行
# 计算加法运算的表达式
addition = lambda x, y : x + y
# 根据传递的整数数据,返回性别描述
# 0 - 女,1 - 男
gender = lambda n : "男" if n == 1 else "女"
表达式作用:
- 简化只包含一行代码的函数,通过表达式简化代码,提高代码的可读性
4.3、 lambda 表达式 & 高级函数
需求 1:需要对列表中的整数数据进行排序
lst = [12, 39, 19, 41, 32, 47, 36, 42, 25, 37, 16, 31, 17, 10, 23, 33, 35, 40, 22, 30]
def sort_fn(x):
return -x
# 指定自定义排序方案
lst.sort(key=sort_fn)
print(lst)
# lambda表达式优化
lst = [12, 39, 19, 41, 32, 47, 36, 42, 25, 37, 16, 31, 17, 10, 23, 33, 35, 40, 22, 30]
# 指定自定义排序方案
lst.sort(key=lambda x: -x)
print(lst)
需求 2:对集合中记录的账号进行过滤,将符合条件的账号收集起来
# 账号收集 6~12位
accounts = {
"damu", "xiaoli", "admin", "root",
"manager", "administrator", "honghh",
"ljh", "www", "qqy", "chunyang"
}
# 传统
def check_account(account):
val_account = set()
for i in account:
if 6 <= len(i) <= 12:
val_account.add(i)
return val_account
print("有效的账号:", check_account(accounts))
# lambda表达式,结合高阶函数filter()
# filter(参数1,参数2)
# 参数1:条件函数,函数返回结果为True表示这个数据保留
# 参数2:序列数据,将序列数据进行遍历,遍历到的每个数据交给参数1的函数进行验证
val_account = set(filter(lambda x: 6 <= len(x) <= 12, accounts))
print(val_account)
需求 3:计算 0~100 的和
def sum(start, end):
"""总和"""
total = 0
for i in range(start, end + 1):
total += i
return total
print(sum(0, 100))
# lambda表达式、结合reduce()高阶函数(累加)
from functools import reduce
# reduce(参数1,参数2)
# 参数1:是一个累加函数
# 参数2:是一个序列数据,自动遍历序列数据,将每个数据交给参数1累加
sum = reduce(lambda x, y: x + y, range(0, 101))
print(sum)
05. 偏函数
5.1、 实际需求
需求:某公司小李开发了一个功能函数,可以将目标数据转换成对应的进制
def covert_num(x, base):
"""进制 转化"""
if base == 2:
return bin(x)
elif base == 8:
return oct(x)
elif base == 16:
return hex(x)
else:
return "没有这个进制"
print(covert_num(10, 16))
print(covert_num(8, 2))
print(covert_num(255, 2))
print(covert_num(19, 8))
上述代码的功能性正常,但是企业要求更高:
-
问题 1:每次转换进制,都需要手工输入对应的进制,调用麻烦
-
问题 2:每次调用这个函数,需要了解一下参数的含义才能明确要传递什么数据
-
问题 3:函数使用的时候,2 进制转换比较多的,其他进制转换比较少;2 进制转换也需要传递 base=2 比较麻烦
5.2、 认识偏函数
解决问题 3:对于经常使用 2 进制转换,可以通过函数参数的默认值进行解决
def covert_num(x, base=2):
"""进制 转化"""
if base == 2:
return bin(x)
elif base == 8:
return oct(x)
elif base == 16:
return hex(x)
else:
return "没有这个进制"
print(covert_num(10, 16))
print(covert_num(8)) # 默认转换2进制
print(covert_num(255,)) # 默认转换2进制
print(covert_num(19, 8))
对于问题 1 和问题 2:需要将函数进行改造才能实现
-
手工封装多个函数,实现对原有函数的扩展!
def covert_num(x, base):
passdef covert_num_2(x):
return covert_num(x, base=2)
def covert_num_8(x):
return covert_num(x, base=8)
def covert_num_16(x):
return covert_num(x, base=16)print(covert_num_16(10))
print(covert_num_2(8)) # 默认转换2进制
print(covert_num_2(255,)) # 默认转换2进制
print(covert_num_8(19))
功能:偏函数,解决函数调用多样性的问题,通过给函数添加额外的参数,解决函数的可用性、可读性!
# 偏函数的扩展
from functools import partial
covert_num_2 = partial(covert_num, base=2)
covert_num_8 = partial(covert_num, base=8)
covert_num_16 = partial(covert_num, base=16)
print(covert_num_16(10))
print(covert_num_2(8)) # 默认转换2进制
print(covert_num_2(255,)) # 默认转换2进制
print(covert_num_8(19))
如果用 lambda 表达式改造?
原本的转换代码:covert_num_16 = partial (covert_num, base=16)
lambda 表达式:covert_num_16 = lambda x: covert_num (x, base=16)
第二部分:Python 面向对象编程(OOP)基础
1 认识面向对象编程思想
1.1 问题引入:从生活场景理解编程思想
通过 "与女朋友爬山"的场景对比,直观理解两种编程思想的核心差异:
| 场景特点 | 核心逻辑 | 优点 | 缺点 | 对应编程思想 |
|---|---|---|---|---|
| 风雨无阻爬山,不受额外事情影响 | 关注爬山流程本身,步骤固定 | 稳定性好 | 扩展性差(无法灵活应对变化) | 面向过程 |
| 可灵活调整计划(换早餐店、新增跑步) | 关注参与对象(你、女朋友)的需求与交互 | 扩展性好 | 稳定性较差(需额外协调变化) | 面向对象 |
1.2 核心概念:什么是面向对象
1.2.1 思想的本质
思想是对已发生问题 和即将发生问题的解决方案的指导,是经过大量实践归纳的解决问题的思路。
1.2.2 面向对象的定义
-
本质 :站在对象的角度看待问题,模拟自然人解决问题的方式;
-
核心逻辑 :通过参与问题解决的对象(事物) ,利用其自身的 特征(属性)和行为(方法) 的协作,完成问题处理;
-
核心价值:不仅能解决当前问题,还能通过对象的复用与扩展,应对更多场景需求。
1.3 面向过程 vs 面向对象
两种编程思想适配不同开发场景,对比如下:
| 对比维度 | 面向过程(POP) | 面向对象(OOP) |
|---|---|---|
| 核心关注 | 解决问题的过程和步骤 | 解决问题的对象、属性与行为 |
| 语法实现 | 函数式编程(通过函数定义步骤,函数间调用实现流程) | 类和对象(通过类封装属性与方法,对象交互实现功能) |
| 核心优势 | 系统稳定性极强 | 系统扩展性极强 |
| 核心劣势 | 系统扩展性极差 | 系统稳定性较差(需额外技术维稳) |
| 适用场景 | 操作系统研发、小型固定脚本 | 电商平台、大型应用、需频繁迭代的项目 |
| 代表语言 | C 语言(纯面向过程) | Java(纯面向对象)、Python(支持两种思想) |
2 类与对象的基础语法
类和对象是面向对象编程的核心载体 :类是抽象模板,对象是模板的具体实例。
2.1 类的声明
通过 class 关键字自定义类型,封装对象的共同属性与方法,遵循固定编码规范。
2.1.1 基础语法与编码规范
class 类名: # 类名:多个英文单词组成,每个单词首字母大写(大驼峰命名)
"""类的文档注释:说明类的功能、用途(类内部第一行)"""
# 1. 属性声明(后续详细讲解)
# 2. 构造方法:初始化对象的成员属性(固定语法 __init__)
def __init__(self, 形参1, 形参2, ...):
"""构造方法:创建对象时自动调用,用于初始化成员属性"""
self.成员属性名1 = 形参1 # self 表示当前对象,绑定成员属性
self.成员属性名2 = 形参2
# 3. 方法声明(后续详细讲解)
def 方法名(self, 形参...):
"""成员方法:对象的行为,第一个参数必须是 self"""
代码逻辑
2.1.2 示例 1:声明 "主机" 类
class Host:
"""主机类:封装主机的名称、IP地址等属性,提供信息获取方法"""
def __init__(self, host_name, host_ip):
"""初始化主机的成员属性:主机名称、IP地址"""
self.host_name = host_name # 成员属性:主机名称
self.host_ip = host_ip # 成员属性:IP地址
def get_info(self):
"""成员方法:获取主机完整信息"""
return f"{self.host_name}: {self.host_ip}"
2.1.3 示例 2:声明 "用户" 类
"""
用户类:封装用户账户信息,提供登录功能
"""
class User:
def __init__(self, username, password):
"""初始化成员属性:用户名、密码"""
self.username = username # 成员属性:用户名
self.password = password # 成员属性:密码
def login(self):
"""成员方法:用户登录验证(仅实现登录单一功能)"""
if self.username == "admin" and self.password == "123":
return True # 登录成功
return False # 登录失败
2.2 对象的创建与使用
对象是类的具体实例,通过类名 + 括号创建,可访问自身属性和调用方法。
2.2.1 核心语法
-
创建对象:
对象引用变量 = 类名(实参1, 实参2, ...)(实参对应__init__形参); -
访问成员属性:
对象引用变量.成员属性名; -
调用成员方法:
对象引用变量.方法名(实参...); -
判断对象关系:
is(是否为同一个对象)、isinstance(对象, 类名)(是否属于某个类)。语法:对象引用变量 = 类名称(实参1, 实参2, ...)
admin_user = User("admin", "123") # 创建用户对象
2.2.2 示例:创建主机对象并操作
if __name__ == '__main__':
# 1. 创建主机对象(自动调用 __init__ 方法)
host100 = Host("host100", "192.168.0.100")
host101 = Host("host101", "192.168.0.101")
# 2. 打印对象(输出内存地址,证明对象是独立的实体)
print(host100)
print(host101)
# 3. 访问成员属性
print(host100.host_name)
print(host101.host_ip)
# 4. 调用成员方法
print(host100.get_info())
print(host101.get_info())
# 5. 判断对象关系
print(host100 is host101)
print(isinstance(host100, Host))
print(isinstance(host101, Host))
2.3 类和对象的核心关系
| 对比维度 | 类(Class) | 对象(Object) |
|---|---|---|
| 本质 | 自定义类型,抽象模板(逻辑上的概念) | 类的具体实例,实际存在的实体(物理上的概念) |
| 特征 | 包含共同属性名和共同方法名,无具体数据 | 包含具体属性值和可执行方法,数据独立 |
| 关系 | 一个类可创建多个对象(一对多) | 一个对象仅属于一个类(多对一) |
| 生活类比 | "人""电脑""动物"(事物的统称) | 屈真、某品牌电脑、某只小狗(具体事物) |
2.4 实战任务
需求:声明 "文件" 类(包含文件名称、文件路径、文件属性),创建 "软连接文件 python3.12" 的对象。
class File:
"""文件类:封装文件的名称、路径、属性"""
def __init__(self, file_name, file_path, file_attr):
"""初始化文件属性:名称、路径、属性"""
self.file_name = file_name # 成员属性:文件名称
self.file_path = file_path # 成员属性:文件路径
self.file_attr = file_attr # 成员属性:文件属性(如"软连接""普通文件")
def get_file_info(self):
"""获取文件完整信息"""
return f"文件名称:{self.file_name}\n文件路径:{self.file_path}\n文件属性:{self.file_attr}"
# 创建软连接文件对象
soft_link_file = File("python3.12", "/usr/bin/python3.12", "软连接文件")
# 访问对象信息
print(soft_link_file.get_file_info())
3 标准类结构与案例
3.1 标准类结构
Python 中标准、完整的类结构包含:类属性、构造方法、实例方法、类方法、静态方法。
class 类名:
# 类属性
属性 = 值
# 构造方法
def __init__(self, 参数):
self.实例属性 = 参数
# 实例方法
def 方法(self):
pass
# 类方法
@classmethod
def 类方法(cls):
pass
# 静态方法
@staticmethod
def 静态方法():
pass
3.2 学生类案例
class Student:
school = "成都文理学院" # 类属性
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
def show_info(self):
print(f"{self.name} {self.age}岁 {self.score}分")
@classmethod
def change_school(cls, new_school):
cls.school = new_school
@staticmethod
def is_pass(score):
return score >= 60
3.3 对象创建与使用
# 创建对象
stu1 = Student("张三", 18, 85)
# 调用实例方法
stu1.show_info()
# 调用类方法修改学校
Student.change_school("成都大学")
四、面向对象三大特性
4.1 封装
4.1.1 封装的核心概念
-
定义:隐藏对象内部的实现细节,只对外提供公开的访问接口(方法)
-
作用:保护数据安全、避免外部随意修改、规范数据操作方式
-
核心语法 :使用双下划线
__定义私有属性 / 方法,外部无法直接访问
4.1.2 私有属性与私有方法
-
私有属性:
self.__属性名 = 值 -
私有方法:
def __方法名(self): -
访问规则:类内部可以访问,类外部无法直接访问 / 修改,必须通过类提供的公开方法操作
4.1.3 案例:银行账户封装
class BankAccount:
def __init__(self, balance=0):
self.__balance = balance # 私有属性:账户余额,外部无法直接修改
# 公开方法:存款
def deposit(self, money):
if money > 0:
self.__balance += money
print(f"存款成功,当前余额:{self.__balance}")
else:
print("存款金额无效!")
# 公开方法:取款
def withdraw(self, money):
if 0 < money <= self.__balance:
self.__balance -= money
print(f"取款成功,当前余额:{self.__balance}")
else:
print("取款金额无效或余额不足!")
# 公开方法:查询余额
def get_balance(self):
return self.__balance
# 测试封装
if __name__ == '__main__':
account = BankAccount(1000)
account.deposit(500) # 存款
account.withdraw(300) # 取款
print(account.get_balance()) # 查询余额
# print(account.__balance) # 报错:外部无法访问私有属性
4.1.4 封装的优点
-
数据安全:防止外部非法修改私有属性
-
代码规范:统一通过方法操作数据,便于维护
-
简化使用:外部无需关注内部实现,只需调用公开接口
4.2 继承
4.2.1 继承的核心概念
继承是面向对象三大特征之一,核心价值是代码复用 和功能扩展,允许子类(派生类)复用父类(基类)的属性和方法,同时添加自身独有的属性和方法。
-
子类继承父类,复用代码
-
super()调用父类方法 -
支持多继承(谨慎使用)
4.2.2 概念与专业术语
| 术语 | 定义 | 示例 |
|---|---|---|
| 父类(基类 / 超类) | 被继承的类,封装了子类的共同属性和方法 | Person 类(封装 "姓名、年龄" 等共同属性) |
| 子类(派生类) | 继承父类的类,可复用父类资源,也可扩展自身资源 | Student 类(继承 Person,新增 "成绩" 属性) |
| 继承 | 子类获取父类属性和方法的过程 | class Student (Person): 表示 Student 继承 Person |
| 派生 | 子类在继承父类的基础上,新增属性或方法的过程 | Student 类新增 study () 方法,属于派生 |
4.2.3 继承的基础语法
4.2.3.1 单继承语法
单继承指子类仅继承一个父类,语法简洁、逻辑清晰,是企业开发的主流选择。
# 父类(基类):封装共同属性和方法
class 父类名:
def __init__(self, 父类属性1, 父类属性2, ...):
self.父类属性1 = 父类属性1
self.父类属性2 = 父类属性2
def 父类方法(self):
代码逻辑
# 子类(派生类):继承父类,可扩展自身属性和方法
class 子类名(父类名):
def __init__(self, 父类属性1, 父类属性2, ..., 子类属性1, 子类属性2, ...):
# 调用父类的构造方法,初始化父类属性(必须优先执行)
super().__init__(父类属性1, 父类属性2, ...)
# 初始化子类独有的属性
self.子类属性1 = 子类属性1
self.子类属性2 = 子类属性2
# 子类独有的方法(派生)
def 子类方法(self):
代码逻辑
# 可选:重写父类方法(覆盖父类逻辑)
def 父类方法(self):
新的代码逻辑
4.2.3.2 示例:单继承实战(学生继承自 "人")
# 父类:Person(封装"人"的共同属性和方法)
class Person:
def __init__(self, name, age):
self.name = name # 父类属性:姓名
self.age = age # 父类属性:年龄
def eat(self):
"""父类方法:吃饭(所有人的共同行为)"""
print(f"{self.name}({self.age}岁)在吃饭")
# 子类:Student(继承 Person,扩展学生独有属性和方法)
class Student(Person):
def __init__(self, name, age, student_id, score):
# 调用父类构造方法,初始化父类属性(name、age)
super().__init__(name, age)
# 子类独有属性:学号、成绩
self.student_id = student_id
self.score = score
def study(self):
"""子类独有方法:学习(学生的特有行为)"""
print(f"学号:{self.student_id},姓名:{self.name},成绩:{self.score} → 正在学习")
def eat(self):
"""重写父类方法:学生的吃饭行为(覆盖父类逻辑)"""
print(f"学生 {self.name}({self.age}岁)在学校食堂吃饭")
# 测试单继承
if __name__ == '__main__':
# 创建学生对象
stu = Student(name="张三", age=18, student_id="2024001", score=90)
# 1. 访问父类属性
print("姓名:", stu.name)
print("年龄:", stu.age)
# 2. 访问子类属性
print("学号:", stu.student_id)
print("成绩:", stu.score)
# 3. 调用父类方法(已重写)
stu.eat()
# 4. 调用子类方法
stu.study()
4.2.3.3 多继承语法
多继承指子类继承多个父类,语法为 class 子类名(父类1, 父类2, ...):,但因易导致逻辑混乱(如菱形继承问题),企业开发中尽量避免使用。
# 父类1:Person(封装"人"的基础属性)
class Person:
def __init__(self, name):
self.name = name
def eat(self):
print(f"{self.name} 在吃饭")
# 父类2:Worker(封装"工作者"的属性)
class Worker:
def __init__(self, job):
self.job = job
def work(self):
print(f"{self.name} 的工作是 {self.job}")
# 子类:Teacher(继承 Person 和 Worker)
class Teacher(Person, Worker):
def __init__(self, name, job, subject):
# 调用多个父类的构造方法
Person.__init__(self, name)
Worker.__init__(self, job)
# 子类独有属性:教授科目
self.subject = subject
def teach(self):
print(f"{self.name} 教授 {self.subject}")
# 测试多继承
teacher = Teacher(name="李四", job="教师", subject="Python")
teacher.eat()
teacher.work()
teacher.teach()
4.2.4 继承中的魔法属性与魔法方法(继承相关)
魔法属性和魔法方法是 Python 内置的特殊属性 / 方法(以 __ 开头和结尾),继承场景中常用以下几个:
4.2.4.1 常用魔法属性
| 魔法属性 | 功能 | 示例 |
|---|---|---|
__base__ |
返回子类的直接父类(单继承) | Student.base |
__bases__ |
返回子类的所有直接父类(多继承) | Teacher.bases |
__mro__ |
返回子类的方法解析顺序(解决多继承冲突) | Teacher.mro |
4.2.4.2 常用魔法方法(继承相关)
| 魔法方法 | 功能 | 示例 |
|---|---|---|
__init__ |
构造方法,初始化属性(子类需调用父类的 init) | super().init(name, age) |
__str__ |
字符串格式化方法,打印对象时返回友好信息 | 重写后 print (stu) 可输出学生详情 |
4.2.4.3 示例:重写 __str__ 魔法方法
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
class Student(Person):
def __init__(self, name, age, student_id):
super().__init__(name, age)
self.student_id = student_id
def __str__(self):
"""重写 __str__ 方法,打印对象时返回友好信息"""
return f"学生信息:[学号:{self.student_id},姓名:{self.name},年龄:{self.age}岁]"
# 测试
stu = Student(name="王五", age=19, student_id="2024002")
print(stu)
4.2.5 继承的核心优势与注意事项
4.2.5.1 核心优势
-
代码复用:子类无需重复编写父类已有的属性和方法,减少冗余代码
-
功能扩展:子类可在父类基础上新增属性和方法,或重写父类方法,适配特定需求
-
逻辑清晰:通过继承建立类之间的层级关系,符合现实世界的分类逻辑
4.2.5.2 注意事项
-
子类必须优先调用父类的构造方法(
super().__init__),否则父类属性无法初始化 -
多继承易导致方法名冲突,需通过
__mro__确认方法解析顺序,尽量避免使用 -
继承层级不宜过深(建议不超过 3 层),否则会增加代码维护难度
4.3 多态
4.3.1 多态的核心概念
-
定义 :一个接口,多种实现;不同子类对象调用同一个方法,执行不同的逻辑
-
实现前提 :必须满足继承 + 方法重写两个条件
-
核心作用:提高代码的通用性、扩展性和灵活性
4.3.2 多态的实现原理
-
子类继承同一个父类
-
子类重写父类的同一个方法
-
不同子类对象调用该方法,表现出不同的行为
4.3.3 基础案例:动物叫声(多态实现)
# 父类:动物类
class Animal:
def speak(self):
"""父类方法:定义统一接口"""
pass
# 子类1:狗类(重写speak方法)
class Dog(Animal):
def speak(self):
print("汪汪汪")
# 子类2:猫类(重写speak方法)
class Cat(Animal):
def speak(self):
print("喵喵喵")
# 子类3:羊类(重写speak方法)
class Sheep(Animal):
def speak(self):
print("咩咩咩")
# 统一调用接口(多态核心)
def make_sound(animal: Animal):
animal.speak()
# 测试多态
if __name__ == '__main__':
dog = Dog()
cat = Cat()
sheep = Sheep()
# 同一个方法,不同实现
make_sound(dog) # 输出:汪汪汪
make_sound(cat) # 输出:喵喵喵
make_sound(sheep) # 输出:咩咩咩
4.3.4 多态的优点
-
代码解耦:不依赖具体子类,只依赖父类接口,降低代码耦合度
-
易于扩展:新增子类时,无需修改原有代码,直接重写方法即可
-
通用性强:统一调用方式,适配所有继承父类的子类对象
4.3.5 多态的适用场景
-
统一接口设计(如:不同支付方式、不同文件读取、不同动物行为)
-
框架 / 工具类开发(屏蔽底层实现,对外提供统一调用方式)
-
大型项目模块化开发(降低模块间依赖)
4.4 面向对象三大特性总结
| 特性 | 核心作用 | 关键字 / 语法 | 核心价值 |
|---|---|---|---|
| 封装 | 保护数据、隐藏实现 | __私有属性、公开方法 |
数据安全、代码规范 |
| 继承 | 代码复用、功能扩展 | class 子类(父类)、super() |
减少冗余、逻辑清晰 |
| 多态 | 统一接口、多种实现 | 继承 + 方法重写 | 灵活扩展、通用调用 |
五、属性与方法
5.1 静态属性与静态方法
静态属性和静态方法本质是"封装在类内部的全局变量和函数",用于实现逻辑隔离,方便代码维护与复用,与具体对象无关。
5.1.1 问题引入:代码整理需求
某服务器维护脚本包含大量分散的函数和变量,难以分类管理和查找(如磁盘处理、网络处理、内存处理函数混合),需通过面向对象语法实现逻辑隔离。
5.1.2 核心语法
class 类名:
"""包含静态属性和静态方法的类"""
# 静态属性:声明在类内部、方法外部的变量
静态属性名 = 属性值
# 静态方法:@staticmethod 装饰,无 self/cls 参数
@staticmethod
def 静态方法名(形参...):
"""静态方法:独立功能函数"""
代码逻辑
5.1.3 示例:服务器维护脚本(逻辑隔离版)
"""
服务器维护脚本:通过静态属性/方法实现功能逻辑隔离
"""
class Disk:
"""磁盘处理类"""
DISK_MAX_USAGE = 0.8
@staticmethod
def disk_io():
print("执行磁盘读写维护...")
@staticmethod
def disk_clean():
print("执行磁盘清理...")
class Net:
"""网络处理类"""
NET_PERCENT = 0.6
@staticmethod
def net_io():
print("执行网络吞吐量监测...")
class Memory:
"""内存处理类"""
MEMORY_MAX_W = 0.9
@staticmethod
def memory_warning():
print(f"内存告警阈值:{Memory.MEMORY_MAX_W}")
class Cpu:
"""CPU处理类"""
@staticmethod
def cpu_usage():
print("执行CPU监测...")
5.1.4 调用方式
-
访问静态属性:
类名.静态属性名 -
调用静态方法:
类名.静态方法名() -
无需创建对象,不推荐用对象调用
if name == 'main':
print(Disk.DISK_MAX_USAGE)
Disk.disk_io()
Net.net_io()
Memory.memory_warning()
Cpu.cpu_usage()
5.1.5 示例:文章操作类
class Article:
max_words = 2000
@staticmethod
def publisher(title, content, publish_time, author):
print(f"===== 发表文章 =====")
print(f"标题:{title}")
@staticmethod
def modify(title, new_content):
print(f"===== 修改文章 =====")
@staticmethod
def delete(title):
print(f"===== 删除文章 =====")
# 使用
print(Article.max_words)
Article.publisher("Python 3.13发布","内容","2024-11-28","gf")
5.1.6 核心说明
- 本质:静态属性 = 类内全局变量,静态方法 = 类内工具函数
- 共享性:所有对象共享,修改后全局生效
- 适用场景:脚本开发、简单工具函数、功能逻辑分组
5.2 类属性与类方法
类属性和类方法是类的 **"公共资源"**,被所有对象共享,用于统一配置、共享数据。
5.2.1 问题引入:共享属性需求
开发文章类,要求:
-
所有文章最大字数相同
-
所有文章出版社信息统一
-
提供统一方法获取共享信息
5.2.2 核心语法
class 类名:
# 类属性:全大写命名
类属性名 = 属性值
@classmethod
def 类方法名(cls, 形参...):
"""cls 代表当前类"""
print(cls.类属性名)
5.2.3 示例:文章类(类属性 / 类方法)
class Article:
MAX_WORDS = 2000
PUBLISH_HOUSE = "XXX出版"
def __init__(self, title, content, author):
self.title = title
self.content = content
self.author = author
@classmethod
def get_publish_info(cls):
print(f"出版社:{cls.PUBLISH_HOUSE}")
5.2.4 示例:课程类
class PythonAI:
COURSE_NAME = "Python AI 综合应用设计"
def __init__(self, student_cnt, teacher, cycle):
self.student_cnt = student_cnt
self.teacher = teacher
self.cycle = cycle
@classmethod
def introduce(cls):
print(f"课程名称:{cls.COURSE_NAME}")
def get_class_info(self):
print(f"讲师:{self.teacher},人数:{self.student_cnt}")
5.2.5 使用方式
# 类名直接访问(推荐)
print(PythonAI.COURSE_NAME)
PythonAI.introduce()
# 对象也可访问(不推荐)
stu = PythonAI(90,"大牧",50)
print(stu.COURSE_NAME)
5.2.6 适用场景
-
类属性:共享常量、配置阈值、全局参数
-
类方法:操作类属性、统一业务逻辑
-
优势:修改一处,全部对象同步更新
5.3 成员属性与成员方法
成员属性 / 方法是对象独有的资源,每个对象数据独立,互不干扰,是面向对象最核心用法。
5.3.1 核心概念
-
成员属性:对象自己的变量(姓名、年龄、账号)
-
成员方法:对象自己的行为(登录、学习、展示信息)
-
依赖
self,必须通过对象调用
5.3.2 核心语法
class 类名:
def __init__(self, 形参1, 形参2):
self.成员属性1 = 形参1
self.成员属性2 = 形参2
def 成员方法(self):
print(self.成员属性1)
5.3.3 示例:人类
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def study(self, way):
print(f"{self.name}({self.age}):{way}")
5.3.4 示例:用户对象操作
class User:
def __init__(self, username, password):
self.username = username
self.password = password
def login(self):
return self.username == "admin" and self.password == "123"
# 使用
user = User("admin","123")
print(user.username)
user.password = "111"
print(user.login())
5.3.5 核心特点
-
独立性:每个对象数据隔离
-
依赖性:必须通过对象访问,类无法直接调用
-
适用场景:业务系统、多实例对象、个性化数据
5.4 三种属性 / 方法对比
5.4.1 属性对比表
| 类型 | 定义位置 | 访问方式 | 共享性 | 影响范围 |
|---|---|---|---|---|
| 静态属性 | 类内、方法外 | 类名 / 对象 | 全局共享 | 全部生效 |
| 类属性 | 类内、方法外 | 类名 / 对象 | 类内共享 | 全部对象 |
| 成员属性 | __init__内 |
仅对象 | 对象独立 | 仅当前对象 |
5.4.2 方法对比表
| 方法类型 | 装饰器 | 默认参数 | 可访问数据 |
|---|---|---|---|
| 成员方法 | 无 | self | 成员属性 + 类属性 |
| 类方法 | @classmethod | cls | 类属性 |
| 静态方法 | @staticmethod | 无 | 无(工具函数) |
5.4.3 快速选择指南
-
存对象独有数据 → 成员属性
-
存全类共享数据 → 类属性
-
写工具函数、分组功能 → 静态方法
-
写操作共享配置 → 类方法
-
写对象自身行为 → 成员方法
5.5 实战案例:图书管理系统
class Book:
def __init__(self, book_id, title, author, stock):
self.book_id = book_id
self.title = title
self.author = author
self.stock = stock
self.borrowed = 0
def borrow(self):
if self.stock > self.borrowed:
self.borrowed +=1
return "借阅成功"
return "库存不足"
class Library:
def __init__(self):
self.books = []
def add_book(self, book):
self.books.append(book)
def find_book(self, book_id):
for b in self.books:
if b.book_id == book_id:
return b
return None
5.6 综合案例:用户认证系统
5.6.1 需求
-
普通用户 / 管理员
-
登录、查看信息
-
管理员可增删用户
-
密码加密存储
5.6.2 代码实现
import hashlib
class User:
def __init__(self, username, password):
self.username = username
self.password = self._encrypt(password)
@staticmethod
def _encrypt(pwd):
md5 = hashlib.md5()
md5.update(pwd.encode())
return md5.hexdigest()
def login(self, user, pwd):
return self.username == user and self.password == self._encrypt(pwd)
def show_info(self):
print(f"用户:{self.username},角色:普通用户")
class Admin(User):
def __init__(self, username, password):
super().__init__(username, password)
self.user_list = []
def create_user(self, username, password):
self.user_list.append(User(username, password))
def delete_user(self, username):
for u in self.user_list:
if u.username == username:
self.user_list.remove(u)
5.6.3 测试
admin = Admin("admin","admin123")
admin.create_user("user1","123456")
admin.show_info()