🎯 今日目标

  • 掌握位置参数和关键字参数

  • 理解不定长参数:*args 和 **kwargs

  • 学会返回多个值(元组自动打包/解包)

  • 理解函数的文档字符串(docstring)

  • 学会使用 lambda 匿名函数

  • 了解函数的嵌套


📌 一、位置参数 vs 关键字参数

1.1 位置参数

我们昨天用的就是位置参数:调用时按照定义时的顺序传递参数。

python

def introduce(name, age, city):
    print(f"我叫{name},{age}岁,来自{city}")

# 按照位置传递:name="张三", age=18, city="北京"
introduce("张三", 18, "北京")

运行结果:

text

我叫张三,18岁,来自北京

1.2 关键字参数

调用时明确指定参数名 = 值,顺序可以打乱。

python

introduce(city="上海", name="李四", age=20)

运行结果:

text

我叫李四,20岁,来自上海

💡 关键字参数让代码更清晰,尤其是参数很多的时候。而且可以跳过默认参数(后面会看到)。

1.3 位置参数和关键字参数混用

规则:位置参数必须写在关键字参数前面。

python

# 正确:位置参数在前,关键字参数在后
introduce("王五", city="深圳", age=22)

# 错误:关键字参数在前,位置参数在后
# introduce(city="深圳", "王五", 22)  # 报错 SyntaxError

🎯 二、默认参数值(复习 + 进阶)

默认参数在定义时赋值,调用时可以省略。

python

def greet(name, greeting="你好"):
    print(f"{greeting},{name}!")

greet("张三")           # 使用默认的"你好"
greet("李四", "早上好")  # 覆盖默认值
greet(name="王五", greeting="晚上好")  # 关键字参数方式

运行结果:

text

你好,张三!
早上好,李四!
晚上好,王五!

⚠️ 默认参数的陷阱:默认值不要用可变对象

python

# 错误示例:默认参数用列表
def add_item(item, items=[]):   # 危险!
    items.append(item)
    return items

print(add_item("苹果"))   # ['苹果']
print(add_item("香蕉"))   # ['苹果', '香蕉'] —— 预期应该是 ['香蕉']

原因:默认参数在函数定义时就被创建了,之后每次调用共用同一个列表。

正确做法:用 None 作为默认值,在函数内部创建新列表。

python

def add_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

print(add_item("苹果"))   # ['苹果']
print(add_item("香蕉"))   # ['香蕉']

📦 三、不定长参数:*args 和 **kwargs

有时候你不知道调用者会传多少个参数,这时候可以用不定长参数。

3.1 *args —— 接收任意数量的位置参数

*args 会把所有多余的位置参数打包成一个元组(tuple)

python

def sum_all(*args):
    total = 0
    for num in args:
        total += num
    return total

print(sum_all(1, 2, 3))       # 6
print(sum_all(10, 20, 30, 40)) # 100
print(sum_all())              # 0(不传参数也可以)

运行结果:

text

6
100
0

💡 *args 的名字可以任意取,但约定俗成叫 args(arguments 的缩写)。* 才是关键符号。

3.2 **kwargs —— 接收任意数量的关键字参数

**kwargs 会把所有多余的关键字参数打包成一个字典(dict)

python

def print_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print_info(name="张三", age=18, city="北京")
print_info(title="Python", author="Guido", year=1991)

运行结果:

text

name: 张三
age: 18
city: 北京
title: Python
author: Guido
year: 1991

💡 **kwargs 的名字也可以任意取,kwargs 是 keyword arguments 的缩写。

3.3 同时使用普通参数、*args 和 **kwargs

顺序必须是:普通参数 → *args → **kwargs

python

def mixed(name, *args, **kwargs):
    print(f"姓名: {name}")
    print(f"args: {args}")
    print(f"kwargs: {kwargs}")

mixed("张三", 85, 92, 78, city="北京", age=18)

运行结果:

text

姓名: 张三
args: (85, 92, 78)
kwargs: {'city': '北京', 'age': 18}

📤 四、返回多个值

Python 函数可以返回多个值,实际上是返回一个元组,Python 会自动帮你打包和解包。

python

def get_min_max(numbers):
    min_val = min(numbers)
    max_val = max(numbers)
    return min_val, max_val   # 实际上返回元组 (min_val, max_val)

# 解包接收
min_num, max_num = get_min_max([3, 1, 8, 2, 5])
print(f"最小值: {min_num}, 最大值: {max_num}")

运行结果:

text

最小值: 1, 最大值: 8

python

# 也可以直接接收整个元组
result = get_min_max([3, 1, 8, 2, 5])
print(result)          # (1, 8)
print(type(result))    # <class 'tuple'>

💡 这个特性非常实用:比如你想从函数中同时返回平均值和总和、或者返回状态和错误信息。


📝 五、文档字符串(docstring)

在函数内部的第一行写一段多行字符串,用来描述函数的作用、参数和返回值,方便自己和他人阅读。

python

def calculate_bmi(weight, height):
    """
    计算身体质量指数(BMI)
    
    参数:
        weight: 体重(公斤)
        height: 身高(米)
    
    返回:
        float: BMI 值
    """
    return weight / (height ** 2)

# 查看函数的文档
help(calculate_bmi)

运行结果:

text

Help on function calculate_bmi in module __main__:

calculate_bmi(weight, height)
    计算身体质量指数(BMI)
    
    参数:
        weight: 体重(公斤)
        height: 身高(米)
    
    返回:
        float: BMI 值

💡 在 VS Code 中,将鼠标悬停在函数名上,会直接显示文档字符串。养成写文档的好习惯!


⚡ 六、lambda 匿名函数

6.1 什么是 lambda?

lambda 是一种没有名字的、只有一行的函数,也叫“匿名函数”。适合用在只需要临时使用、逻辑简单的场景。

语法:

python

lambda 参数列表: 表达式

等同于:

python

def 函数名(参数列表):
    return 表达式

6.2 基本例子

python

# 普通函数
def add(a, b):
    return a + b

# lambda 写法
add_lambda = lambda a, b: a + b

print(add(3, 5))          # 8
print(add_lambda(3, 5))   # 8

💡 lambda 的表达式会自动作为返回值,不需要写 return

6.3 经典使用场景:配合 sorted() 排序

lambda 最常用在需要传入函数的场景,比如排序时指定“按什么排序”。

python

students = [
    {"name": "张三", "score": 85},
    {"name": "李四", "score": 92},
    {"name": "王五", "score": 78},
]

# 按分数升序排序
sorted_by_score = sorted(students, key=lambda student: student["score"])
for s in sorted_by_score:
    print(f"{s['name']}: {s['score']}分")

运行结果:

text

王五: 78分
张三: 85分
李四: 92分

python

# 按名字长度排序
words = ["apple", "banana", "cherry", "date"]
sorted_words = sorted(words, key=lambda w: len(w))
print(sorted_words)   # ['date', 'apple', 'banana', 'cherry']

🪆 七、函数的嵌套

可以在函数内部再定义函数,内部函数只在外部函数内部可见。

python

def outer(name):
    def inner(greeting):
        print(f"{greeting},{name}!")
    return inner   # 把内部函数本身返回出去

# 调用外部函数,得到内部函数
f = outer("张三")
f("你好")     # 输出:你好,张三!
f("早上好")   # 输出:早上好,张三!

运行结果:

text

你好,张三!
早上好,张三!

💡 这种“函数返回函数”的写法是闭包(Closure)的雏形。现在了解即可,以后深入学习时会用到。


🧪 八、综合示例:学生成绩工具集

python

# 第9天综合示例:学生成绩工具集

def get_stats(scores):
    """
    计算成绩统计信息
    
    参数:
        scores: 数字列表
    
    返回:
        tuple: (总分, 平均值, 最大值, 最小值)
    """
    total = sum(scores)
    avg = total / len(scores)
    max_score = max(scores)
    min_score = min(scores)
    return total, avg, max_score, min_score

def filter_scores(scores, threshold=60):
    """筛选出高于 threshold 的成绩"""
    return [s for s in scores if s >= threshold]

def show_students(students, **kwargs):
    """
    显示学生信息,支持按条件过滤
    
    参数:
        students: 学生字典列表
        **kwargs: 过滤条件,如 name="张三", score_min=80
    """
    # 应用过滤条件
    filtered = students
    for key, value in kwargs.items():
        if key == "score_min":
            filtered = [s for s in filtered if s["score"] >= value]
        elif key == "name":
            filtered = [s for s in filtered if s["name"] == value]
    
    # 显示结果
    if not filtered:
        print("没有匹配的学生")
        return
    
    print("符合条件的同学:")
    for s in filtered:
        print(f"  {s['name']}: {s['score']}分")

# 使用示例
students = [
    {"name": "张三", "score": 85},
    {"name": "李四", "score": 92},
    {"name": "王五", "score": 78},
    {"name": "赵六", "score": 55},
]

# 统计
scores = [s["score"] for s in students]
total, avg, max_s, min_s = get_stats(scores)
print(f"全班总分: {total}")
print(f"平均分: {avg:.1f}")
print(f"最高分: {max_s}")
print(f"最低分: {min_s}")

# 筛选及格同学
passed = filter_scores(scores)
print(f"及格人数: {len(passed)}")

# 条件查询
show_students(students, score_min=80)
show_students(students, name="李四")

运行结果:

text

全班总分: 310
平均分: 77.5
最高分: 92
最低分: 55
及格人数: 3
符合条件的同学:
  张三: 85分
  李四: 92分
  王五: 78分
符合条件的同学:
  李四: 92分

🐛 九、新手常见错误及解决

错误现象错误代码示例解决方法
参数顺序错误introduce(age=18, "张三")(关键字参数在前)位置参数必须在关键字参数之前
默认参数被共享def f(items=[]) 导致累加用 items=None 代替
*args 和 **kwargs 位置搞反def f(**kwargs, *args)顺序必须是 *args 在前,**kwargs在后
忘记 lambda 只能写表达式lambda x: return x*2return 是多余的,直接写 lambda x: x*2
嵌套函数外部无法访问在外部调用内部函数只能通过外部函数返回内部函数来使用
混淆 args 和 kwargs的用途用 *args 接收关键字参数*args 收位置参数,**kwargs 收关键字参数

错误演示:默认参数陷阱

python

def add_student(name, class_list=[]):   # 问题在这里
    class_list.append(name)
    return class_list

class_a = add_student("张三")
class_b = add_student("李四")
print(class_a)   # 期望 ['张三'],实际 ['张三', '李四']
print(class_b)   # ['张三', '李四']

修正:

python

def add_student(name, class_list=None):
    if class_list is None:
        class_list = []
    class_list.append(name)
    return class_list

class_a = add_student("张三")  # ['张三']
class_b = add_student("李四")  # ['李四']

🎯 今日总结

今天你学会了:

  • 位置参数和关键字参数

  • 默认参数及其注意事项(可变默认值陷阱)

  • 不定长参数:*args(接收位置参数)、**kwargs(接收关键字参数)

  • 函数返回多个值(元组自动打包/解包)

  • 文档字符串(docstring)的写法

  • lambda 匿名函数及其常见用法(排序等)

  • 函数的嵌套

更多推荐