第6章 函数及其应用
6.2 函数的参数传递

📚 本节学习目标
通过本节学习,你将能够: - 理解Python中不可变类型与可变类型的区别 - 掌握函数参数传递的两种基本机制(值传递与引用传递) - 学会使用关键字参数(按参数名传递)提高代码可读性 - 掌握默认参数的设置方法与使用技巧 - 能够根据实际需求选择合适的参数传递方式

🔗 知识回顾与衔接
在正式进入本节内容之前,让我们先回顾一下前面章节的重要知识点,这些知识将帮助我们更好地理解函数参数传递的机制:
第2章回顾: - Python的数据类型:数值型(int, float)、字符串(str)、布尔型(bool) - 变量的概念:变量是对象的引用
第5章回顾: - 组合数据类型:元组(tuple)、列表(list)、字典(dict)、集合(set) - 元组的不可变性:元组一旦创建,其元素不能被修改 - 列表的可变性:列表可以随时添加、删除、修改元素
💡 重要概念:在Python中,变量实际上是对象的”引用”或”标签”,而不是存储数据的”盒子”。理解这一点对掌握参数传递至关重要!

6.2.1 不改变实参值的参数传递
一、核心概念讲解
1. 什么是不可变类型?
不可变类型(Immutable Types) 是指一旦创建后,其值就不能被修改的数据类型。当我们试图”修改”不可变类型的值时,实际上是在创建一个新的对象。
2. Python中的不可变类型包括:
数据类型 | 说明 | 示例 |
int | 整型 | age = 20 |
float | 浮点型 | price = 3.14 |
bool | 布尔型 | flag = True |
str | 字符串 | name = "CK大学" |
tuple | 元组 | coords = (106.5, 29.6) |
3. 参数传递机制详解
当调用函数时,如果传入的实参是不可变类型: - 函数内部对形参的修改不会影响实参的值 - 这是因为形参实际上是实参的一个”副本引用” - 当修改形参时,Python会创建一个新的对象,而实参仍然指向原来的对象
二、代码案例演示
案例1:整型参数传递(以CK大学学生成绩为例)
# 定义一个函数:尝试修改学生的成绩def modify_score(score): """ 尝试修改学生成绩 参数score:学生的原始成绩 """ print(f"函数内部 - 修改前score的值: {score}, id: {id(score)}") score = 95 # 尝试将成绩修改为95分 print(f"函数内部 - 修改后score的值: {score}, id: {id(score)}")# 主程序original_score = 85print(f"调用函数前 - original_score的值: {original_score}, id: {id(original_score)}")print("-" * 50)modify_score(original_score)print("-" * 50)print(f"调用函数后 - original_score的值: {original_score}, id: {id(original_score)}")
运行结果分析:
调用函数前 - original_score的值: 85, id: 140735893619024--------------------------------------------------函数内部 - 修改前score的值: 85, id: 140735893619024函数内部 - 修改后score的值: 95, id: 140735893619344--------------------------------------------------调用函数后 - original_score的值: 85, id: 140735893619024
关键发现: - 修改前,实参和形参的id相同,说明它们指向同一个对象 - 修改后,形参的id改变了,说明创建了新对象 - 实参的id始终不变,说明实参仍然指向原来的对象
📝 知识点串联:id()函数我们在第2章学习过,它返回对象的唯一标识符(内存地址)。通过比较id,我们可以判断两个变量是否指向同一个对象。

案例2:浮点型参数传递(以重庆火锅价格计算为例)
def calculate_discount(price): """ 计算打折后的价格(重庆火锅店促销活动) 参数price:原价 """ print(f"🍲 火锅店原价: {price}元") price = price * 0.8 # 打8折 print(f"🎉 打折后价格: {price}元") return price# 主程序original_price = 128.0 # 重庆某火锅店套餐原价print(f"调用函数前,套餐价格: {original_price}元")print("=" * 40)new_price = calculate_discount(original_price)print("=" * 40)print(f"调用函数后,原套餐价格: {original_price}元")print(f"函数返回值(新价格): {new_price}元")
运行结果:
调用函数前,套餐价格: 128.0元========================================🍲 火锅店原价: 128.0元🎉 打折后价格: 102.4元========================================调用函数后,原套餐价格: 128.0元函数返回值(新价格): 102.4元
结论:函数内部的修改不影响外部变量,如果需要使用修改后的值,必须通过return返回。

案例3:字符串参数传递(以CK大学学生信息为例)
def format_student_name(name): """ 格式化学生姓名 参数name:学生原始姓名 """ print(f"函数内部 - 接收到的name: '{name}', id: {id(name)}") name = name.strip().title() # 去除空格并首字母大写 print(f"函数内部 - 格式化后的name: '{name}', id: {id(name)}") return name# 主程序student_name = " zhang san " # 带有空格的姓名print(f"调用函数前 - student_name: '{student_name}'")print("-" * 50)formatted_name = format_student_name(student_name)print("-" * 50)print(f"调用函数后 - student_name: '{student_name}'")print(f"函数返回的formatted_name: '{formatted_name}'")
运行结果:
调用函数前 - student_name: 'zhang san'--------------------------------------------------函数内部 - 接收到的name: 'zhang san', id: 2456789012345函数内部 - 格式化后的name: 'Zhang San', id: 2456789012456--------------------------------------------------调用函数后 - student_name: 'zhang san'函数返回的formatted_name: 'Zhang San'
📝 知识点串联:字符串的strip()和title()方法我们在第2章学习过。字符串是不可变类型,这些方法都会返回新的字符串,而不是修改原字符串。

案例4:元组参数传递(以重庆地理坐标为例)
def update_coordinates(coords): """ 尝试更新地理坐标 参数coords:包含经纬度的元组 """ print(f"函数内部 - 原始坐标: {coords}") print(f"函数内部 - coords的id: {id(coords)}") # 尝试修改元组的第一个元素(这会报错吗?) # coords[0] = 107.0 # 如果取消注释,会报错:TypeError: 'tuple' object does not support item assignment # 正确的做法:创建新的元组 coords = (107.0, coords[1]) print(f"函数内部 - 新坐标: {coords}") print(f"函数内部 - 新coords的id: {id(coords)}")# 主程序:重庆的地理坐标(大约)chongqing_coords = (106.55, 29.57) # 经度, 纬度print(f"调用函数前 - 重庆坐标: {chongqing_coords}")print(f"调用函数前 - chongqing_coords的id: {id(chongqing_coords)}")print("-" * 50)update_coordinates(chongqing_coords)print("-" * 50)print(f"调用函数后 - 重庆坐标: {chongqing_coords}")print(f"调用函数后 - chongqing_coords的id: {id(chongqing_coords)}")
运行结果:
调用函数前 - 重庆坐标: (106.55, 29.57)调用函数前 - chongqing_coords的id: 2456789012567--------------------------------------------------函数内部 - 原始坐标: (106.55, 29.57)函数内部 - coords的id: 2456789012567函数内部 - 新坐标: (107.0, 29.57)函数内部 - 新coords的id: 2456789012678--------------------------------------------------调用函数后 - 重庆坐标: (106.55, 29.57)调用函数后 - chongqing_coords的id: 2456789012567
📝 知识点串联:元组是不可变类型,我们不能通过索引修改其元素。在第5章中我们学过,元组使用圆括号()定义,一旦创建就不能修改。

案例5:布尔型参数传递(以CK大学课程及格判断为例)
def toggle_status(is_passed): """ 切换考试状态 参数is_passed:当前及格状态 """ print(f"函数内部 - 接收到的状态: {is_passed}, id: {id(is_passed)}") is_passed = not is_passed # 取反 print(f"函数内部 - 切换后的状态: {is_passed}, id: {id(is_passed)}") return is_passed# 主程序student_passed = True # 学生初始状态:及格print(f"调用函数前 - 学生及格状态: {student_passed}")print("-" * 50)new_status = toggle_status(student_passed)print("-" * 50)print(f"调用函数后 - 学生及格状态: {student_passed}")print(f"函数返回的新状态: {new_status}")
运行结果:
调用函数前 - 学生及格状态: True--------------------------------------------------函数内部 - 接收到的状态: True, id: 140735892879696函数内部 - 切换后的状态: False, id: 140735892879728--------------------------------------------------调用函数后 - 学生及格状态: True函数返回的新状态: False

三、本节小结
不可变类型 | 修改方式 | 是否影响实参 |
int, float | 赋值运算 | ❌ 不影响 |
bool | 逻辑运算 | ❌ 不影响 |
str | 字符串方法 | ❌ 不影响 |
tuple | 重新创建 | ❌ 不影响 |
记忆口诀: > 🎯 “数值布尔字符串,元组不可变,函数里面怎么改,外面都不变!”

6.2.2 改变实参值的参数传递
一、核心概念讲解
1. 什么是可变类型?
可变类型(Mutable Types) 是指创建后可以被修改的数据类型。我们可以直接修改其内容,而不需要创建新的对象。
2. Python中的可变类型包括:
数据类型 | 说明 | 示例 |
list | 列表 | scores = [85, 90, 78] |
dict | 字典 | student = {"name": "张三", "age": 20} |
set | 集合 | courses = {"Python", "数学", "英语"} |
3. 参数传递机制详解
当调用函数时,如果传入的实参是可变类型: - 函数内部对形参的修改会影响实参的值 - 这是因为形参和实参指向同一个对象 - 当通过形参修改对象内容时,实参也会”看到”这些变化
二、代码案例演示
案例1:列表参数传递(以CK大学学生成绩管理为例)
def add_bonus_score(scores): """ 给每个学生成绩加5分(平时表现加分) 参数scores:学生成绩列表 """ print(f"函数内部 - 原始成绩: {scores}") print(f"函数内部 - scores的id: {id(scores)}") # 使用for循环遍历并修改列表元素 for i in range(len(scores)): scores[i] = scores[i] + 5 # 每个成绩加5分 print(f"函数内部 - 加分后成绩: {scores}") print(f"函数内部 - scores的id: {id(scores)}")# 主程序class_scores = [78, 82, 91, 85, 88] # CK大学某班级5名学生的成绩print(f"调用函数前 - 班级成绩: {class_scores}")print(f"调用函数前 - class_scores的id: {id(class_scores)}")print("-" * 50)add_bonus_score(class_scores)print("-" * 50)print(f"调用函数后 - 班级成绩: {class_scores}")print(f"调用函数后 - class_scores的id: {id(class_scores)}")
运行结果:
调用函数前 - 班级成绩: [78, 82, 91, 85, 88]调用函数前 - class_scores的id: 2456789012789--------------------------------------------------函数内部 - 原始成绩: [78, 82, 91, 85, 88]函数内部 - scores的id: 2456789012789函数内部 - 加分后成绩: [83, 87, 96, 90, 93]函数内部 - scores的id: 2456789012789--------------------------------------------------调用函数后 - 班级成绩: [83, 87, 96, 90, 93]调用函数后 - class_scores的id: 2456789012789
关键发现: - 函数内部和外部,id始终相同,说明是同一个对象 - 函数内部修改后,外部的列表也被修改了 - 这是引用传递的典型特征
📝 知识点串联:range(len(scores))我们在第4章循环中学习过,它生成一个索引序列。scores[i]是列表索引访问,我们在第5章学习过。

案例2:列表的append操作(以CK大学课程选修为例)
def add_elective_course(courses, new_course): """ 添加选修课程 参数courses:已选课程列表 参数new_course:新课程名称 """ print(f"📚 当前已选课程: {courses}") courses.append(new_course) # 使用append方法添加元素 print(f"✅ 添加新课程后: {courses}") print(f"函数内部 - courses的id: {id(courses)}")# 主程序my_courses = ["Python基础", "高等数学", "大学英语"] # 已选课程print(f"调用函数前 - 我的课程: {my_courses}")print(f"调用函数前 - my_courses的id: {id(my_courses)}")print("-" * 50)add_elective_course(my_courses, "数据分析")print("-" * 50)print(f"调用函数后 - 我的课程: {my_courses}")print(f"调用函数后 - my_courses的id: {id(my_courses)}")
运行结果:
调用函数前 - 我的课程: ['Python基础', '高等数学', '大学英语']调用函数前 - my_courses的id: 2456789012890--------------------------------------------------📚 当前已选课程: ['Python基础', '高等数学', '大学英语']✅ 添加新课程后: ['Python基础', '高等数学', '大学英语', '数据分析']函数内部 - courses的id: 2456789012890--------------------------------------------------调用函数后 - 我的课程: ['Python基础', '高等数学', '大学英语', '数据分析']调用函数后 - my_courses的id: 2456789012890
📝 知识点串联:append()方法我们在第5章学习过,它是列表的重要方法,用于在列表末尾添加元素。注意append()没有返回值,直接修改原列表。

案例3:字典参数传递(以CK大学学生信息管理为例)
def update_student_info(student, key, value): """ 更新学生信息 参数student:学生信息字典 参数key:要更新的字段 参数value:新的值 """ print(f"函数内部 - 原始信息: {student}") print(f"函数内部 - student的id: {id(student)}") student[key] = value # 修改字典的值 print(f"函数内部 - 更新后信息: {student}") print(f"函数内部 - student的id: {id(student)}")# 主程序student_info = { "学号": "20230101", "姓名": "李明", "专业": "数字经济", "成绩": 85} # CK大学某学生信息print(f"调用函数前 - 学生信息: {student_info}")print(f"调用函数前 - student_info的id: {id(student_info)}")print("-" * 50)update_student_info(student_info, "成绩", 92)print("-" * 50)print(f"调用函数后 - 学生信息: {student_info}")print(f"调用函数后 - student_info的id: {id(student_info)}")
运行结果:
调用函数前 - 学生信息: {'学号': '20230101', '姓名': '李明', '专业': '数字经济', '成绩': 85}调用函数前 - student_info的id: 2456789012901--------------------------------------------------函数内部 - 原始信息: {'学号': '20230101', '姓名': '李明', '专业': '数字经济', '成绩': 85}函数内部 - student的id: 2456789012901函数内部 - 更新后信息: {'学号': '20230101', '姓名': '李明', '专业': '数字经济', '成绩': 92}函数内部 - student的id: 2456789012901--------------------------------------------------调用函数后 - 学生信息: {'学号': '20230101', '姓名': '李明', '专业': '数字经济', '成绩': 92}调用函数后 - student_info的id: 2456789012901
📝 知识点串联:字典的键值对访问student[key]我们在第5章学习过。字典是可变类型,可以通过键来修改对应的值。

案例4:集合参数传递(以CK大学社团成员管理为例)
def join_club(members, new_member): """ 添加社团成员 参数members:社团成员集合 参数new_member:新成员姓名 """ print(f"函数内部 - 当前成员: {members}") print(f"函数内部 - members的id: {id(members)}") members.add(new_member) # 使用add方法添加成员 print(f"函数内部 - 添加后成员: {members}") print(f"函数内部 - members的id: {id(members)}")# 主程序python_club = {"张三", "李四", "王五"} # CK大学Python社团现有成员print(f"调用函数前 - 社团成员: {python_club}")print(f"调用函数前 - python_club的id: {id(python_club)}")print("-" * 50)join_club(python_club, "赵六")print("-" * 50)print(f"调用函数后 - 社团成员: {python_club}")print(f"调用函数后 - python_club的id: {id(python_club)}")
运行结果:
调用函数前 - 社团成员: {'张三', '李四', '王五'}调用函数前 - python_club的id: 2456789013012--------------------------------------------------函数内部 - 当前成员: {'张三', '李四', '王五'}函数内部 - members的id: 2456789013012函数内部 - 添加后成员: {'张三', '李四', '王五', '赵六'}函数内部 - members的id: 2456789013012--------------------------------------------------调用函数后 - 社团成员: {'张三', '李四', '王五', '赵六'}调用函数后 - python_club的id: 2456789013012
📝 知识点串联:集合的add()方法我们在第5章学习过。集合是无序且不重复的数据结构,使用花括号{}定义。

案例5:综合案例 - CK大学图书馆借阅系统
def borrow_book(library_books, borrowed_list, book_name): """ 借阅图书 参数library_books:图书馆藏书列表(可变类型) 参数borrowed_list:个人借阅列表(可变类型) 参数book_name:要借阅的书名(不可变类型) """ print(f"📖 当前图书馆藏书: {library_books}") print(f"📚 当前个人借阅: {borrowed_list}") print(f"🔍 要借阅的书: '{book_name}'") if book_name in library_books: library_books.remove(book_name) # 从图书馆移除 borrowed_list.append(book_name) # 添加到个人借阅 print(f"✅ 成功借阅《{book_name}》") else: print(f"❌ 图书馆没有《{book_name}》") print(f"📖 借阅后图书馆藏书: {library_books}") print(f"📚 借阅后个人借阅: {borrowed_list}")# 主程序ck_library = ["Python编程", "经济学原理", "数据分析", "机器学习"] # CK大学图书馆藏书my_books = [] # 我的借阅列表(初始为空)target_book = "Python编程" # 想借的书print("=" * 50)print("🏫 CK大学图书馆借阅系统")print("=" * 50)print(f"调用函数前 - 图书馆: {ck_library}")print(f"调用函数前 - 我的借阅: {my_books}")print("-" * 50)borrow_book(ck_library, my_books, target_book)print("-" * 50)print(f"调用函数后 - 图书馆: {ck_library}")print(f"调用函数后 - 我的借阅: {my_books}")print(f"书名变量target_book: '{target_book}'")
运行结果:
==================================================🏫 CK大学图书馆借阅系统==================================================调用函数前 - 图书馆: ['Python编程', '经济学原理', '数据分析', '机器学习']调用函数前 - 我的借阅: []--------------------------------------------------📖 当前图书馆藏书: ['Python编程', '经济学原理', '数据分析', '机器学习']📚 当前个人借阅: []🔍 要借阅的书: 'Python编程'✅ 成功借阅《Python编程》📖 借阅后图书馆藏书: ['经济学原理', '数据分析', '机器学习']📚 借阅后个人借阅: ['Python编程']--------------------------------------------------调用函数后 - 图书馆: ['经济学原理', '数据分析', '机器学习']调用函数后 - 我的借阅: ['Python编程']书名变量target_book: 'Python编程'
重要观察: - library_books和borrowed_list(可变类型)在函数内外都被修改了 - book_name(不可变类型)在函数内部是独立的,不影响外部的target_book

三、本节小结
可变类型 | 修改方式 | 是否影响实参 |
list | 索引修改、append、remove等 | ✅ 会影响 |
dict | 键值对修改 | ✅ 会影响 |
set | add、remove等 | ✅ 会影响 |
记忆口诀: > 🎯 “列表字典和集合,可变类型要记住,函数里面一改,外面跟着变!”

四、不可变 vs 可变类型对比总结
# 综合对比演示
def demonstrate_difference(): """对比不可变类型和可变类型的参数传递""" # 不可变类型示例 num = 100 string = "CK大学" tup = (1, 2, 3) # 可变类型示例 lst = [1, 2, 3] dct = {"a": 1, "b": 2} st = {1, 2, 3} print("=" * 60) print("🔴 不可变类型(函数内部修改不影响外部)") print("=" * 60) def modify_immutable(n, s, t): n = 200 s = "重庆大学" t = (4, 5, 6) print(f"函数内部: num={n}, string={s}, tup={t}") modify_immutable(num, string, tup) print(f"函数外部: num={num}, string={string}, tup={tup}") print("\n" + "=" * 60) print("🟢 可变类型(函数内部修改影响外部)") print("=" * 60) def modify_mutable(l, d, s): l.append(4) d["c"] = 3 s.add(4) print(f"函数内部: lst={l}, dct={d}, st={s}") modify_mutable(lst, dct, st) print(f"函数外部: lst={lst}, dct={dct}, st={st}")demonstrate_difference()
运行结果:
============================================================🔴 不可变类型(函数内部修改不影响外部)============================================================函数内部: num=200, string=重庆大学, tup=(4, 5, 6)函数外部: num=100, string=CK大学, tup=(1, 2, 3)============================================================🟢 可变类型(函数内部修改影响外部)============================================================函数内部: lst=[1, 2, 3, 4], dct={'a': 1, 'b': 2, 'c': 3}, st={1, 2, 3, 4}函数外部: lst=[1, 2, 3, 4], dct={'a': 1, 'b': 2, 'c': 3}, st={1, 2, 3, 4}

6.2.3 按参数名传递参数
一、核心概念讲解
1. 默认的参数传递方式:位置参数
在默认情况下,Python函数调用时,实参与形参之间按照位置和顺序一一对应传递值。这种方式称为位置参数传递。
def student_info(name, age, major): print(f"姓名: {name}, 年龄: {age}, 专业: {major}")# 位置参数传递:按顺序对应student_info("张三", 20, "数字经济")# 输出: 姓名: 张三, 年龄: 20, 专业: 数字经济
2. 什么是按参数名传递?
按参数名传递(也称为关键字参数传递)是指在函数调用时,通过形参名=值的形式明确指定参数的值。
这种方式的优点: - ✅ 可以打乱顺序传递参数 - ✅ 代码可读性更强,一看就知道每个参数的含义 - ✅ 避免因为参数顺序错误导致的bug
二、代码案例演示
案例1:基本使用(以CK大学学生注册为例)
def register_student(name, age, major, city): """ 学生注册函数 参数name: 姓名 参数age: 年龄 参数major: 专业 参数city: 所在城市 """ print("🎓 CK大学学生注册信息") print(f" 姓名: {name}") print(f" 年龄: {age}") print(f" 专业: {major}") print(f" 城市: {city}") print("-" * 40)# 方式1:位置参数传递(必须按顺序)print("【方式1】位置参数传递:")register_student("王芳", 19, "数字经济", "重庆")# 方式2:按参数名传递(可以打乱顺序)print("【方式2】按参数名传递(打乱顺序):")register_student(city="重庆", major="数字经济", age=19, name="王芳")# 方式3:混合使用(位置参数在前,关键字参数在后)print("【方式3】混合使用:")register_student("王芳", 19, city="重庆", major="数字经济")
运行结果:
【方式1】位置参数传递:🎓 CK大学学生注册信息姓名: 王芳年龄: 19专业: 数字经济城市: 重庆----------------------------------------【方式2】按参数名传递(打乱顺序):🎓 CK大学学生注册信息姓名: 王芳年龄: 19专业: 数字经济城市: 重庆----------------------------------------【方式3】混合使用:🎓 CK大学学生注册信息姓名: 王芳年龄: 19专业: 数字经济城市: 重庆----------------------------------------
重要规则: > ⚠️ 混合使用时,位置参数必须在前,关键字参数必须在后,否则会报错!
# ❌ 错误示例:关键字参数在位置参数前面# register_student(name="王芳", 19, major="数字经济", city="重庆")# SyntaxError: positional argument follows keyword argument

案例2:提高代码可读性(以重庆旅游行程规划为例)
def plan_trip(start, destination, days, budget): """ 规划重庆旅游行程 参数start: 出发地 参数destination: 目的地 参数days: 游玩天数 参数budget: 预算(元) """ print("🚄 重庆旅游行程规划") print(f" 从 {start} 出发") print(f" 目的地: {destination}") print(f" 游玩天数: {days}天") print(f" 预算: {budget}元") print(f" 日均消费: {budget/days:.2f}元/天") print("-" * 40)# ❌ 不使用参数名:可读性差,容易混淆顺序print("❌ 不使用参数名(可读性差):")plan_trip("成都", "重庆", 3, 1500)# 如果写成 plan_trip("重庆", "成都", 1500, 3) 就错了,但不容易发现# ✅ 使用参数名:一目了然,不会出错print("✅ 使用参数名(可读性强):")plan_trip(start="成都", destination="重庆", days=3, budget=1500)# ✅ 即使打乱顺序也清晰print("✅ 打乱顺序依然清晰:")plan_trip(budget=2000, days=5, start="武汉", destination="重庆")
运行结果:
❌ 不使用参数名(可读性差):🚄 重庆旅游行程规划从 成都 出发目的地: 重庆游玩天数: 3天预算: 1500元日均消费: 500.00元/天----------------------------------------✅ 使用参数名(可读性强):🚄 重庆旅游行程规划从 成都 出发目的地: 重庆游玩天数: 3天预算: 1500元日均消费: 500.00元/天----------------------------------------✅ 打乱顺序依然清晰:🚄 重庆旅游行程规划从 武汉 出发目的地: 重庆游玩天数: 5天预算: 2000元日均消费: 400.00元/天----------------------------------------

案例3:避免参数顺序错误(以CK大学成绩录入为例)
def record_score(student_id, course_name, score, semester): """ 录入学生成绩 参数student_id: 学号 参数course_name: 课程名 参数score: 分数 参数semester: 学期 """ print(f"📋 成绩录入成功") print(f" 学号: {student_id}") print(f" 课程: {course_name}") print(f" 分数: {score}") print(f" 学期: {semester}") print("-" * 40)# ❌ 错误:参数顺序混乱,导致数据错误print("❌ 位置参数顺序错误(危险!):")# 本意:学号20230101,课程Python,分数90,学期2025-2026-2# 实际:学号变成了"Python",课程变成了90!record_score("Python", 90, "2025-2026-2", "20230101")print()# ✅ 正确:使用参数名,绝对不会错print("✅ 使用参数名(安全可靠):")record_score(student_id="20230101", course_name="Python", score=90, semester="2025-2026-2")
运行结果:
❌ 位置参数顺序错误(危险!):📋 成绩录入成功学号: Python课程: 90分数: 2025-2026-2学期: 20230101----------------------------------------✅ 使用参数名(安全可靠):📋 成绩录入成功学号: 20230101课程: Python分数: 90学期: 2025-2026-2----------------------------------------
🎯 编程建议:当函数有多个参数,或者参数类型相同时(比如都是字符串),强烈建议使用参数名传递,避免错误!

案例4:与前面章节知识的串联(结合第5章的列表和字典)
def analyze_scores(student_name, scores, weights): """ 分析学生成绩(加权平均分) 参数student_name: 学生姓名(字符串-不可变) 参数scores: 各科成绩(列表-可变) 参数weights: 权重(字典-可变) """ print(f"📊 {student_name}的成绩分析") print(f" 各科成绩: {scores}") print(f" 权重设置: {weights}") # 计算加权平均分 total = 0 weight_sum = 0 for i, score in enumerate(scores): course = ["Python", "数学", "英语"][i] weight = weights.get(course, 1.0) total += score * weight weight_sum += weight print(f" {course}: {score}分 × {weight} = {score * weight}") average = total / weight_sum print(f" 加权平均分: {average:.2f}") print("-" * 40) return average# 主程序student = "张三"score_list = [85, 90, 78]weight_dict = {"Python": 2.0, "数学": 1.5, "英语": 1.0}# 使用参数名调用,清晰明了analyze_scores(student_name=student, scores=score_list, weights=weight_dict)# 修改权重后再次分析weight_dict["Python"] = 3.0 # Python课程权重提高print("📝 修改Python权重后重新分析:")analyze_scores(weights=weight_dict, student_name=student, scores=score_list)
运行结果:
📊 张三的成绩分析各科成绩: [85, 90, 78]权重设置: {'Python': 2.0, '数学': 1.5, '英语': 1.0}Python: 85分 × 2.0 = 170.0数学: 90分 × 1.5 = 135.0英语: 78分 × 1.0 = 78.0加权平均分: 84.62----------------------------------------📝 修改Python权重后重新分析:📊 张三的成绩分析各科成绩: [85, 90, 78]权重设置: {'Python': 3.0, '数学': 1.5, '英语': 1.0}Python: 85分 × 3.0 = 255.0数学: 90分 × 1.5 = 135.0英语: 78分 × 1.0 = 78.0加权平均分: 85.09----------------------------------------
📝 知识点串联:enumerate()函数我们在第4章学习过,它用于同时获取索引和元素。字典的get()方法我们在第5章学习过,用于安全地获取值。

三、本节小结
传递方式 | 语法 | 优点 | 使用场景 |
位置参数 | func(1, 2, 3) | 简洁快速 | 参数少且顺序明确 |
关键字参数 | func(a=1, b=2, c=3) | 可读性强,顺序灵活 | 参数多或类型相同 |
混合使用 | func(1, b=2, c=3) | 兼顾简洁和可读性 | 前几个参数明确,后面的需要说明 |
重要规则: > ⚠️ 位置参数必须在关键字参数之前!
记忆口诀: > 🎯 “参数传递有讲究,位置关键字两相宜,位置在前关键字后,代码清晰不出错!”

6.2.4 按默认值传递参数
一、核心概念讲解
1. 什么是默认参数?
默认参数是指在定义函数时,为参数指定一个默认值。如果调用函数时没有提供该参数的值,就使用默认值。
2. 默认参数的语法
def 函数名(形参名1=默认值1, 形参名2=默认值2, ...): 函数体
3. 默认参数的优点
·✅ 简化函数调用:常用参数不需要每次都写
·✅ 提高代码灵活性:可以根据需要覆盖默认值
·✅ 向后兼容:添加新参数时不会影响旧代码
二、代码案例演示
案例1:基本使用(以CK大学学生信息登记为例)
def register_student(name, age=18, major="数字经济", city="重庆"): """ 学生信息登记 参数name: 姓名(必须提供) 参数age: 年龄(默认18) 参数major: 专业(默认"数字经济") 参数city: 城市(默认"重庆") """ print("🎓 CK大学学生信息登记") print(f" 姓名: {name}") print(f" 年龄: {age}") print(f" 专业: {major}") print(f" 城市: {city}") print("-" * 40)# 方式1:只提供必须参数,其他使用默认值print("【方式1】只提供姓名:")register_student("张三")# 方式2:提供部分参数,覆盖相应默认值print("【方式2】提供姓名和年龄:")register_student("李四", age=20)# 方式3:使用关键字参数,跳过某些默认参数print("【方式3】使用关键字参数:")register_student("王五", city="成都")# 方式4:提供所有参数,覆盖所有默认值print("【方式4】提供所有参数:")register_student("赵六", 19, "金融学", "北京")
运行结果:
【方式1】只提供姓名:🎓 CK大学学生信息登记姓名: 张三年龄: 18专业: 数字经济城市: 重庆----------------------------------------【方式2】提供姓名和年龄:🎓 CK大学学生信息登记姓名: 李四年龄: 20专业: 数字经济城市: 重庆----------------------------------------【方式3】使用关键字参数:🎓 CK大学学生信息登记姓名: 王五年龄: 18专业: 数字经济城市: 成都----------------------------------------【方式4】提供所有参数:🎓 CK大学学生信息登记姓名: 赵六年龄: 19专业: 金融学城市: 北京----------------------------------------

案例2:重庆地铁票价计算(实际应用场景)
def calculate_subway_fare(distance, base_price=2, price_per_km=0.5, max_price=10): """ 计算重庆地铁票价 参数distance: 乘坐距离(公里)- 必须提供 参数base_price: 起步价(默认2元) 参数price_per_km: 每公里价格(默认0.5元) 参数max_price: 最高票价(默认10元) """ fare = base_price + distance * price_per_km if fare > max_price: fare = max_price print("🚇 重庆地铁票价计算") print(f" 乘坐距离: {distance}公里") print(f" 起步价: {base_price}元") print(f" 每公里: {price_per_km}元") print(f" 最高票价: {max_price}元") print(f" 💰 应付票价: {fare:.1f}元") print("-" * 40) return fare# 普通乘客:使用默认票价print("【普通乘客】从解放碑到观音桥(5公里):")calculate_subway_fare(5)# 学生票:起步价优惠print("【学生票】从解放碑到大学城(20公里):")calculate_subway_fare(20, base_price=1)# 老年卡:每公里更便宜print("【老年卡】从沙坪坝到江北机场(30公里):")calculate_subway_fare(30, price_per_km=0.3)# 定制需求:使用关键字参数print("【定制需求】从南坪到渝北(15公里):")calculate_subway_fare(distance=15, base_price=3, price_per_km=0.6, max_price=15)
运行结果:
【普通乘客】从解放碑到观音桥(5公里):🚇 重庆地铁票价计算乘坐距离: 5公里起步价: 2元每公里: 0.5元最高票价: 10元💰 应付票价: 4.5元----------------------------------------【学生票】从解放碑到大学城(20公里):🚇 重庆地铁票价计算乘坐距离: 20公里起步价: 1元每公里: 0.5元最高票价: 10元💰 应付票价: 10.0元----------------------------------------【老年卡】从沙坪坝到江北机场(30公里):🚇 重庆地铁票价计算乘坐距离: 30公里起步价: 2元每公里: 0.3元最高票价: 10元💰 应付票价: 10.0元----------------------------------------【定制需求】从南坪到渝北(15公里):🚇 重庆地铁票价计算乘坐距离: 15公里起步价: 3元每公里: 0.6元最高票价: 15元💰 应付票价: 12.0元----------------------------------------

案例3:默认参数的位置规则(重要!)
# ✅ 正确:默认参数在右侧,非默认参数在左侧def correct_function(a, b, c=3, d=4): print(f"a={a}, b={b}, c={c}, d={d}")# ❌ 错误:默认参数不能放在非默认参数左侧# def wrong_function(a=1, b, c, d=4):# print(f"a={a}, b={b}, c={c}, d={d}")# SyntaxError: non-default argument follows default argument# 测试正确函数print("✅ 正确的参数顺序:")correct_function(1, 2) # a=1, b=2, c=3, d=4correct_function(1, 2, 30) # a=1, b=2, c=30, d=4correct_function(1, 2, 30, 40) # a=1, b=2, c=30, d=40
运行结果:
✅ 正确的参数顺序:a=1, b=2, c=3, d=4a=1, b=2, c=30, d=4a=1, b=2, c=30, d=40
重要规则: > ⚠️ 所有指定默认值的参数必须放在右侧,未指定默认值的必须放在左侧,不可以混放!
记忆口诀: > 🎯 “默认参数放右边,非默认的放左边,顺序混乱会报错,牢记规则不犯错!”

案例4:可变类型作为默认参数的陷阱(进阶注意点)
# ⚠️ 警告:不要使用可变类型作为默认参数!def add_course_wrong(student_name, courses=[]): """ 错误示例:使用列表作为默认参数 """ courses.append("Python") print(f"{student_name}的课程: {courses}")print("❌ 错误示例(使用可变类型作为默认参数):")add_course_wrong("张三") # 期望: ['Python']add_course_wrong("李四") # 期望: ['Python'],实际: ['Python', 'Python']add_course_wrong("王五") # 期望: ['Python'],实际: ['Python', 'Python', 'Python']print()# ✅ 正确做法:使用None作为默认值def add_course_correct(student_name, courses=None): """ 正确示例:使用None作为默认参数 """ if courses is None: courses = [] courses.append("Python") print(f"{student_name}的课程: {courses}")print("✅ 正确做法(使用None作为默认参数):")add_course_correct("张三") # ['Python']add_course_correct("李四") # ['Python']add_course_correct("王五") # ['Python']
运行结果:
❌ 错误示例(使用可变类型作为默认参数):张三的课程: ['Python']李四的课程: ['Python', 'Python']王五的课程: ['Python', 'Python', 'Python']✅ 正确做法(使用None作为默认参数):张三的课程: ['Python']李四的课程: ['Python']王五的课程: ['Python']
原因解释: > Python的默认参数在函数定义时只被计算一次。如果使用可变类型(如列表)作为默认值,所有调用都会共享同一个对象!
📝 知识点串联:None是Python的特殊值,表示”空”或”无”。我们在第2章学习过,它是NoneType类型的唯一值。

案例5:综合案例 - CK大学成绩管理系统
def record_grade(student_id, course="Python基础", score=0, semester="2025-2026-2", recorder="系统"): """ CK大学成绩管理系统 - 录入成绩 参数student_id: 学号(必须提供) 参数course: 课程名(默认"Python基础") 参数score: 分数(默认0) 参数semester: 学期(默认"2025-2026-2") 参数recorder: 录入人(默认"系统") """ grade_info = { "学号": student_id, "课程": course, "分数": score, "学期": semester, "录入人": recorder, "状态": "及格" if score >= 60 else "不及格" } print("📋 成绩录入成功") for key, value in grade_info.items(): print(f" {key}: {value}") print("-" * 40) return grade_info# 场景1:补考录入(只提供学号和分数)print("【场景1】补考成绩录入:")record_grade("20230101", score=75)# 场景2:正常考试录入(提供学号、课程、分数)print("【场景2】正常考试成绩录入:")record_grade("20230102", course="高等数学", score=88)# 场景3:特殊情况录入(使用关键字参数跳过某些参数)print("【场景3】特殊情况录入(指定录入人):")record_grade("20230103", score=92, recorder="王老师")# 场景4:完整信息录入(覆盖所有默认值)print("【场景4】完整信息录入:")record_grade("20230104", "大学英语", 85, "2025-2026-1", "李老师")
运行结果:
【场景1】补考成绩录入:📋 成绩录入成功学号: 20230101课程: Python基础分数: 75学期: 2025-2026-2录入人: 系统状态: 及格----------------------------------------【场景2】正常考试成绩录入:📋 成绩录入成功学号: 20230102课程: 高等数学分数: 88学期: 2025-2026-2录入人: 系统状态: 及格----------------------------------------【场景3】特殊情况录入(指定录入人):📋 成绩录入成功学号: 20230103课程: Python基础分数: 92学期: 2025-2026-2录入人: 王老师状态: 及格----------------------------------------【场景4】完整信息录入:📋 成绩录入成功学号: 20230104课程: 大学英语分数: 85学期: 2025-2026-1录入人: 李老师状态: 及格----------------------------------------
📝 知识点串联:字典的items()方法我们在第5章学习过,它返回字典的所有键值对。三元表达式"及格" if score >= 60 else "不及格"我们在第3章分支结构中学习过。

三、本节小结
默认参数使用规则
规则 | 说明 | 示例 |
语法 | def func(a, b=默认值) | def greet(name, msg="你好") |
位置 | 默认参数必须在右侧 | ✅ def f(a, b=1) ❌ def f(a=1, b) |
调用 | 可以提供也可以不提供 | greet("张三") 或 greet("张三", "Hello") |
注意 | 不要用可变类型做默认值 | ✅ def f(a, b=None) ❌ def f(a, b=[]) |
默认参数 + 关键字参数组合使用
def advanced_example(a, b=2, c=3, d=4): print(f"a={a}, b={b}, c={c}, d={d}")# 各种调用方式advanced_example(1) # 位置参数 + 全部默认advanced_example(1, 20) # 位置参数 + 部分默认advanced_example(1, c=30) # 位置参数 + 关键字参数跳过badvanced_example(1, d=40, b=20) # 完全打乱顺序
记忆口诀: > 🎯 “默认参数真方便,常用值不用传,放在右边记心间,None代替可变类型更安全!”

📊 本节知识总结
一、四种参数传递方式对比
传递方式 | 语法特点 | 是否影响实参 | 使用建议 |
不可变类型传递 | 实参为int/float/str/tuple | ❌ 不影响 | 需要返回值来获取修改结果 |
可变类型传递 | 实参为list/dict/set | ✅ 会影响 | 直接修改,无需return |
按参数名传递 | func(a=1, b=2) | 取决于类型 | 参数多时提高可读性 |
按默认值传递 | def func(a=1) | 取决于类型 | 简化调用,提供灵活性 |
二、快速判断流程图
调用函数时:↓参数是否有默认值?├── 是 → 可以不提供实参 → 使用默认值└── 否 → 必须提供实参↓实参是什么类型?├── 不可变类型(int/float/str/tuple/bool)│└── 函数内部修改不影响外部└── 可变类型(list/dict/set)└── 函数内部修改会影响外部↓如何传递参数?├── 位置参数:func(1, 2, 3) → 按顺序对应└── 关键字参数:func(a=1, b=2) → 顺序可打乱
三、重要注意事项
1.位置参数必须在关键字参数之前
2.默认参数必须在非默认参数之后
3.不要用可变类型(list/dict/set)作为默认参数,使用None代替
4.需要修改外部变量时,使用可变类型或return返回值

🤖 AI辅助学习:用AI加深对本节内容的理解
一、为什么要用AI辅助学习?
在学习编程的过程中,AI可以成为你的: - 🎯 24小时助教:随时解答疑问 - 💡 代码生成器:快速生成示例代码 - 🔍 错误诊断师:帮助找出代码问题 - 📚 知识串联者:帮助联系前后知识点
二、如何使用AI学习本节内容?
方法1:让AI解释概念
你可以这样问AI:
请用通俗易懂的语言解释Python中"不可变类型"和"可变类型"的区别,并用生活中的例子类比说明。
AI可能的回答方向: - 不可变类型就像身份证上的姓名(一旦确定不能修改,只能换新证) - 可变类型就像书包里的书(可以随时添加、移除、更换)

方法2:让AI生成练习题
你可以这样问AI:
我是Python初学者,正在学习函数参数传递。请给我出5道关于"可变类型与不可变类型参数传递"的选择题,并附带详细解析。

方法3:让AI检查你的代码
你可以这样问AI:
请检查下面这段代码是否有问题,并解释原因:def add_item(items=[]):items.append("new")return itemsprint(add_item())print(add_item())

三、有趣的Python练习(配合AI使用)
下面为你设计了5个有趣的编程练习,每个练习都提供了AI提示词,你可以复制这些提示词与AI对话,让AI帮助你完成代码。

练习1:输出爱心图案 ❤️
练习目标:巩固字符串操作和函数参数默认值
AI提示词:
请帮我写一个Python函数print_heart(size=10),函数接收一个参数size控制爱心的大小,默认值为10。使用数学公式或字符画的方式输出一个爱心图案。要求:1. 使用函数封装2. size参数有默认值3. 代码要有详细注释4. 展示不同size的输出效果
预期学习效果: - 理解默认参数的使用 - 复习字符串的乘法操作("*" * n) - 了解数学公式在编程中的应用

练习2:重庆火锅点菜系统 🍲
练习目标:巩固关键字参数和默认参数
AI提示词:
请帮我设计一个重庆火锅点菜系统,使用Python函数实现。函数名:order_hotpot参数:- table_number: 桌号(必须提供)- soup_base: 锅底类型(默认"鸳鸯锅")- spiciness: 辣度(默认"中辣",可选"微辣"/"中辣"/"特辣"/"变态辣")- dishes: 菜品列表(默认空列表)- num_people: 用餐人数(默认2人)- need_apron: 是否需要围裙(默认True)要求:1. 使用默认参数和关键字参数2. 函数内部计算预估消费金额(每个菜平均30元,锅底58元)3. 打印完整的订单信息4. 提供3个不同的调用示例展示各种参数传递方式
预期学习效果: - 掌握多个默认参数的设置 - 练习关键字参数的灵活使用 - 复习列表作为参数的处理

练习3:CK大学成绩分析器 📊
练习目标:巩固可变类型的参数传递
AI提示词:
请帮我写一个CK大学成绩分析器,使用Python实现。功能要求:1. 函数add_score(scores, bonus=5):给成绩列表每个元素加bonus分2. 函数calculate_average(scores):计算平均分3. 函数get_top_students(scores, names, n=3):返回前n名学生的姓名和成绩要求:1. 展示可变类型(列表)作为参数的传递效果2. 展示函数内部修改如何影响外部变量3. 使用默认参数4. 提供完整的测试代码,包括:- 原始成绩列表- 调用add_score后的成绩列表(观察是否被修改)- 计算平均分- 获取前三名
预期学习效果: - 深入理解可变类型的参数传递机制 - 练习列表的各种操作(索引、切片、遍历) - 复习循环和条件语句

练习4:重庆景点推荐系统 🏞️
练习目标:综合运用位置参数、关键字参数、默认参数
AI提示词:
请帮我创建一个重庆景点推荐系统。函数名:recommend_attraction参数:- visitor_type: 游客类型(默认"普通游客",可选"亲子"/"情侣"/"老人"/"学生")- budget: 预算(默认200元)- time_available: 可用时间(默认"半天",可选"半天"/"一天"/"两天")- preferences: 偏好列表(默认["美食", "风景"])- season: 季节(默认"春季")功能:1. 根据参数推荐1-3个重庆景点2. 打印推荐理由3. 计算预估总花费重庆景点数据库(函数内部使用):- 洪崖洞:免费,适合晚上,美食多- 解放碑:免费,购物方便- 磁器口:免费,古镇风情,美食多- 武隆天坑:门票120,自然风光,需要一天- 长江索道:单程20,体验独特- 南山一棵树:门票30,看夜景要求:1. 展示多种参数传递方式的调用示例2. 代码要有清晰的注释3. 输出格式美观
预期学习效果: - 综合运用多种参数传递方式 - 复习列表、字符串的操作 - 练习条件判断(if-elif-else)

练习5:Python代码美化器 ✨
练习目标:巩固不可变类型和可变类型的区别
AI提示词:
请帮我写一个Python代码美化器,用于格式化简单的Python代码字符串。函数1:add_indentation(code_lines, spaces=4)- 参数code_lines:代码行列表(可变类型)- 参数spaces:缩进空格数(默认4)- 功能:给每行代码添加指定数量的空格缩进- 注意:展示列表作为可变类型的修改效果函数2:add_comment(line, comment="# TODO")- 参数line:代码行(字符串,不可变类型)- 参数comment:注释内容(默认"# TODO")- 功能:在代码行末尾添加注释- 注意:展示字符串作为不可变类型的特点函数3:format_code(code, indent=4, add_end_comment=True)- 综合使用上述功能- 返回格式化后的代码列表测试要求:1. 展示函数内部修改对外部变量的影响2. 对比可变类型(列表)和不可变类型(字符串)的差异3. 使用默认参数4. 提供before/after的对比输出
预期学习效果: - 深入理解可变类型与不可变类型的本质区别 - 掌握函数参数传递的底层机制 - 复习字符串和列表的各种方法

四、与AI对话的技巧
技巧1:提供足够的上下文
❌ 不好的提问:
帮我写一个函数。
✅ 好的提问:
我正在学习Python函数参数传递,请帮我写一个演示"可变类型参数传递"的函数,要求使用列表作为参数,展示函数内部修改对外部变量的影响。
技巧2:要求详细的解释
在AI给出代码后,可以继续追问:
请逐行解释这段代码的执行过程,特别是内存中对象的变化。
技巧3:要求对比和总结
请用表格形式总结Python中可变类型和不可变类型的区别,包括:数据类型、修改方式、参数传递效果、典型应用场景。
技巧4:让AI扮演老师角色
请扮演一位Python老师,我正在学习函数参数传递。请用通俗易懂的方式讲解"为什么列表作为参数会被函数修改,而整数不会",并给我出一道练习题检验我的理解。
