Python 函数详解(下)附综合练习题
- 2026-11-05 21:02:14
6. 值传递和引用传递
Python 中所有数据都是对象,变量只是"指向对象的引用(标签)"。当变量作为参数传给函数时,传递的是「对象引用的副本」——但后续操作是否影响原对象,取决于对象本身是"不可变"还是"可变"
不可变对象(如 int、str、tuple):类似"值传递",修改不影响原对象
不可变对象的特点:创建后值无法修改,任何"修改"操作都会生成新的对象(而非改变原对象)。
defmodify_num(num):
num=num+10# 整数是不可变对象,此操作会生成新整数(15)
print("函数内的 num:", num)
a=5
modify_num(a)
print("函数外的 a:", a) # 输出:函数外的 a: 5

解释:
a 是整数(不可变对象),传递给函数时,num 拿到的是「a 指向的整数 5 的引用副本」。
执行 num = num + 10 时,因整数不可变,会创建新的整数对象 15,并让 num 指向它(原对象 5 不受影响)。
因此,函数外的 a 仍指向原对象 5,值不变。
可变对象(如 list、dict、set):类似"引用传递",修改会影响原对象
可变对象的特点:可以在原地修改内容(不会生成新对象,直接修改原对象的内部数据)。
defmodify_list(lst):
lst.append(100) # 列表是可变对象,此操作直接修改原列表
print("函数内的 lst:", lst)
my_list= [1, 2, 3]
modify_list(my_list)
print("函数外的 my_list:", my_list) # 输出:函数外的 my_list: [1, 2, 3, 100]
特殊情况:对可变对象"重新赋值",不影响原对象
若在函数内对可变对象的参数重新赋值(让参数指向新对象),则不会影响外部原对象——因为这是让「引用副本」指向了新对象,而非修改原对象。
defreassign_list(lst):
lst= [10, 20, 30] # 让 lst 指向新列表(原列表不受影响)
print("函数内的 lst:", lst)
my_list= [1, 2, 3]
reassign_list(my_list)
print("函数外的 my_list:", my_list) # 输出:函数外的 my_list: [1, 2, 3]
总结:Python 的参数传递规则
传递的是 "对象引用的副本",而非"值的副本"或"对象本身的引用"
若对象是不可变的(
int、str、tuple等):函数内"修改"会生成新对象,原对象不受影响 → 行为类似"值传递"若对象是可变的(
list、dict、set等):函数内"原地修改内容"会影响原对象 → 行为类似"引用传递";但"重新赋值参数"不会影响原对象
7. 函数的递归
在 Python 中,递归(Recursion) 指的是函数自己调用自己的编程方式。它的核心思想是:把一个复杂问题分解为和原问题"结构相同但规模更小"的子问题,直到子问题小到可以直接解决(终止条件),再通过子问题的解反推原问题的解。
递归的两个核心思想
递归函数必须包含两个部分,否则会陷入"无限递归"(类似死循环):
1.终止条件(BaseCase):当问题小到可以直接解决时,返回一个明确的结果,不再继续调用自己
2.递归步骤(RecursiveCase):将原问题分解为"规模更小的同类子问题",并通过调用自身解决子问题,最终用子问题的解构建原问题的解
阶乘示例
阶乘的数学定义:
n! = n × (n-1) × (n-2) × ... × 1(n ≥ 1)特殊情况:
0! = 1(终止条件)
显然,n! 可以分解为 n × (n-1)!(子问题),符合递归的结构
deffactorial(n):
# 1. 终止条件:n=0时直接返回1
ifn==0:
return1
# 2. 递归步骤:n! = n × (n-1)!,调用自身解决子问题
else:
returnn*factorial(n-1)
# 测试
print(factorial(5)) # 输出:120(5! = 5×4×3×2×1×1 = 120)
print(factorial(0)) # 输出:1(符合终止条件)
递归的优缺点
优点:
代码简洁,逻辑清晰
适合解决分治、树形结构等问题
缺点:
效率较低:每次递归调用都会在内存栈中开辟新空间(存储函数参数、返回地址等),可能导致额外的时间和空间开销
可能栈溢出:Python 对递归深度有默认限制(约 1000 层),超过会抛出
RecursionError调试困难:多层递归的调用栈复杂,出错时难以追踪问题
递归练习题
用递归计算斐波那契数列的第
n项(斐波那契定义:fib(0)=0,fib(1)=1,fib(n)=fib(n-1)+fib(n-2))用递归计算
1 + 2 + 3 + ... + n的和(提示:sum(n) = n + sum(n-1),终止条件sum(1)=1)用递归实现字符串反转(如输入
"abc",输出"cba",提示:reverse(s) = s[-1] + reverse(s[:-1]),终止条件reverse("") = "")
8. 匿名函数
匿名函数指的是"没有名字的函数",它通过 lambda 关键字定义,因此也被称为 lambda 函数。匿名函数的核心作用是快速定义简单逻辑的函数,通常用于"临时需要一个函数"的场景(比如作为其他函数的参数)
基本语法
lambda 参数列表: 表达式
参数列表:函数的输入参数,可以有 0 个或多个参数
表达式:函数的执行逻辑,必须是单个表达式(不能包含循环、多行代码等复杂逻辑),表达式的结果会自动作为函数的返回值(无需写
return)
使用示例
1. 定义匿名函数并赋值给变量
# 定义匿名函数并赋值给变量
add=lambdaa, b: a+b
# 通过变量调用
print(add(3, 5)) # 输出:8(等价于普通函数的调用)
2. 作为其他函数的参数
people= [
{"name": "张三", "age": 20},
{"name": "李四", "age": 18},
{"name": "王五", "age": 22}
]
# 按年龄升序排序,key=lambda x: x["age"] 表示"以字典的'age'值作为排序依据"
sorted_people=sorted(people, key=lambdax: x["age"])
print(sorted_people)
# 输出:[{'name': '李四', 'age': 18}, {'name': '张三', 'age': 20}, {'name': '王五', 'age': 22}]
9. 偏函数
偏函数(Partial Function) 是 functools 模块提供的一个工具,它的核心作用是:固定原函数的部分参数(提前给某些参数赋值),生成一个新的函数。这样在调用新函数时,只需传递剩下的参数,无需重复传递那些固定的参数,让代码更简洁。
使用场景
例如,Python 内置的 int() 函数可以将字符串转换为整数,它有一个可选参数 base(默认值为 10),用于指定字符串的进制:
int("10")→ 默认按 10 进制转换,结果为 10int("10", base=2)→ 按 2 进制转换,结果为 2int("10", base=8)→ 按 8 进制转换,结果为 8
如果你的场景中频繁需要将二进制字符串转整数(即 base=2 固定),每次调用都写 int(s, base=2) 会很重复。这时,偏函数就能帮你"固定 base=2",生成一个新的"二进制转换专用函数"。
基本语法
fromfunctoolsimportpartial
新函数=partial(原函数, 固定参数1=值1, 固定参数2=值2, ...)
使用示例
fromfunctoolsimportpartial
# 固定 int() 的 base=2,生成"二进制转整数"的偏函数
int_bin=partial(int, base=2)
# 调用新函数时,只需传字符串(不用再写 base=2)
print(int_bin("10")) # 输出:2(等价于 int("10", base=2))
print(int_bin("1111")) # 输出:15(等价于 int("1111", base=2))
偏函数练习题
用 functools.partial 对内置函数 max() 进行包装,生成一个"求列表中小于等于 10 的最大值"的偏函数(提示:max() 可以接收 key 参数,如 max(lst, key=lambda x: x if x<=10 else -inf))
10. 函数综合练习题
练习1:学生成绩统计系统
需求:编写一个程序,实现学生成绩的统计功能。具体要求:
用列表存储学生信息,每个学生信息为字典(包含
"name"姓名、"scores"各科成绩列表,如{"name": "张三", "scores": [85, 92, 78]})定义函数
calculate_stats(students),接收学生列表,返回一个统计结果字典,包含:"avg_score":所有学生的平均分(所有科目总分 ÷ 总科目数)"max_score":所有科目中的最高分"min_score":所有科目中的最低分"pass_rate":及格率(60 分及以上的科目占比,保留 2 位小数)定义函数
add_student(students, name, scores),向学生列表中添加新学生(需检查scores是否全为数字,若有非数字则不添加并提示错误)测试:添加 3 名学生,调用统计函数并打印结果
练习2:购物清单管理系统
需求:编写一个程序,模拟购物清单的管理功能。具体要求:
用列表存储购物项,每个购物项为元组
(商品名称, 单价, 数量),如("苹果", 5.9, 2)定义以下函数:
add_item(items, name, price, quantity):添加商品(检查单价和数量是否为正数,若否则不添加并提示错误)remove_item(items, name):移除指定名称的商品(若不存在则提示)calculate_total(items):计算总价(若购物项总数≥5,总价打 9 折;≥10 打 8 折,否则无折扣),返回总价(保留 2 位小数)show_items(items):打印所有商品(格式:序号. 商品名称:单价×数量=小计)测试:添加 5 种商品,移除 1 种,计算总价并打印清单
练习3:物流调度系统
背景:某物流企业有多个仓库,每个仓库存储多种货物(用数字 ID 标识),每种货物有库存数量。企业接到一批订单,需从仓库调货满足订单需求。
核心数据结构
仓库列表
warehouses:元素为字典,结构:{"id": 仓库ID(int), "inventory": {货物ID(int): 库存数量(int)}}订单列表
orders:元素为字典,结构:{"order_id": 订单ID(int), "items": {货物ID(int): 需求数量(int)}}距离矩阵
distances:二维列表,distances[w1][w2]表示仓库w1到仓库w2的运输距离(整数)
需求函数
check_availability(warehouses, orders):检查所有订单的总需求是否可被仓库总库存满足allocate_goods(warehouses, orders, distances):若库存足够,生成调度方案(优先从距离目的地最近的仓库调货)calculate_cost(allocations, distances):根据调度方案计算总运输成本