Python实战第8期:函数

引言:为什么需要函数?

在前面的课程中,我们编写的代码都是按顺序执行的。但有时候,我们需要重复执行某些操作。

比如:

  • 计算多个数的阶乘
  • 格式化输出多个学生的信息
  • 验证多个用户的密码

如果没有函数,我们需要重复写相同的代码。有了函数,我们可以把代码封装起来,需要时直接调用。

函数是代码复用的基础,掌握了函数,你的代码会更简洁、更易维护。


一、函数的定义和调用

1. 什么是函数

函数是一段具有特定功能的、可重用的代码块。通过函数名,我们可以多次调用这段代码。

python 复制代码
# 定义函数
def greet():
    print("Hello, Python!")
    print("欢迎学习函数!")

# 调用函数
greet()
greet()

输出:

复制代码
Hello, Python!
欢迎学习函数!
Hello, Python!
欢迎学习函数!

2. 函数的基本语法

python 复制代码
# 基本语法
# def 函数名(参数列表):
#     函数体
#     return 返回值

# 示例:计算两个数的和
def add(a, b):
    result = a + b
    return result

# 调用函数
sum_result = add(3, 5)
print(sum_result)  # 输出:8

💡 提示:

  • def 关键字用于定义函数
  • 函数名要见名知意,使用小写字母和下划线
  • return 用于返回结果,可以省略(省略时返回None)

3. 函数的文档字符串

python 复制代码
def calculate_area(radius):
    """
    计算圆的面积
    
    参数:
        radius (float): 圆的半径
    
    返回:
        float: 圆的面积
    """
    pi = 3.14159
    return pi * radius ** 2

# 查看函数文档
print(calculate_area.__doc__)

# 调用函数
area = calculate_area(5)
print(f"半径为5的圆面积是:{area:.2f}")

💡 提示 :文档字符串是函数的说明,使用三引号包裹,可以通过 __doc__ 属性查看。

4. 函数的返回值

python 复制代码
# 返回单个值
def square(x):
    return x ** 2

result = square(5)
print(result)  # 输出:25

# 返回多个值(实际上是返回元组)
def get_min_max(numbers):
    return min(numbers), max(numbers)

minimum, maximum = get_min_max([3, 1, 4, 1, 5, 9, 2, 6])
print(f"最小值:{minimum},最大值:{maximum}")

# 没有return语句,返回None
def say_hello():
    print("Hello!")

result = say_hello()
print(result)  # 输出:None

二、参数和返回值

1. 位置参数

python 复制代码
def greet(name, message):
    print(f"{name},{message}")

# 按位置传递参数
greet("小明", "你好!")  # 输出:小明,你好!
greet("你好!", "小明")  # 输出:你好!,小明(参数顺序错了)

2. 关键字参数

python 复制代码
def greet(name, message):
    print(f"{name},{message}")

# 使用关键字参数,可以不按顺序
greet(message="你好!", name="小明")  # 输出:小明,你好!

3. 默认参数

python 复制代码
def greet(name, message="你好!"):
    print(f"{name},{message}")

# 使用默认参数
greet("小明")  # 输出:小明,你好!
greet("小红", "早上好!")  # 输出:小红,早上好!

💡 提示:默认参数必须放在非默认参数后面。

python 复制代码
# ❌ 错误
# def greet(message="你好!", name):  # SyntaxError
#     pass

# ✅ 正确
def greet(name, message="你好!"):
    pass

4. 可变参数

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

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(1, 2, 3, 4, 5))  # 输出:15
print(sum_all())  # 输出:0

# args是一个元组
def print_args(*args):
    print(f"参数类型:{type(args)}")
    print(f"参数内容:{args}")

print_args(1, 2, 3)

输出:

arduino 复制代码
参数类型:<class 'tuple'>
参数内容:(1, 2, 3)

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

python 复制代码
def print_info(**kwargs):
    """打印所有信息"""
    print(f"参数类型:{type(kwargs)}")
    for key, value in kwargs.items():
        print(f"{key}: {value}")

# 调用
print_info(name="小明", age=20, city="北京")

输出:

vbnet 复制代码
参数类型:<class 'dict'>
name: 小明
age: 20
city: 北京

混合使用

python 复制代码
def func(a, b, *args, **kwargs):
    print(f"a: {a}")
    print(f"b: {b}")
    print(f"args: {args}")
    print(f"kwargs: {kwargs}")

func(1, 2, 3, 4, 5, name="小明", age=20)

输出:

yaml 复制代码
a: 1
b: 2
args: (3, 4, 5)
kwargs: {'name': '小明', 'age': 20}

💡 提示:参数顺序必须是:位置参数 → *args → 关键字参数 → **kwargs

5. 参数解包

python 复制代码
# 列表解包
def add(a, b, c):
    return a + b + c

numbers = [1, 2, 3]
result = add(*numbers)  # 解包列表
print(result)  # 输出:6

# 字典解包
def greet(name, age, city):
    print(f"姓名:{name},年龄:{age},城市:{city}")

info = {"name": "小明", "age": 20, "city": "北京"}
greet(**info)  # 解包字典

三、变量的作用域

1. 局部变量和全局变量

python 复制代码
# 全局变量
global_var = "我是全局变量"

def test():
    # 局部变量
    local_var = "我是局部变量"
    print(global_var)  # 可以访问全局变量
    print(local_var)   # 可以访问局部变量

test()
print(global_var)  # 可以访问全局变量
# print(local_var)  # NameError: name 'local_var' is not defined

💡 提示:

  • 全局变量:在函数外部定义,整个程序都可以访问
  • 局部变量:在函数内部定义,只能在函数内部访问

2. 修改全局变量

python 复制代码
count = 0

def increment():
    global count  # 声明要修改全局变量
    count += 1

increment()
increment()
increment()
print(count)  # 输出:3

⚠️ 警告 :尽量少用 global,会使代码难以维护。

3. 作用域规则(LEGB规则)

Python查找变量的顺序:

  1. Local(局部):函数内部
  2. Enclosing(嵌套):外层函数
  3. Global(全局):模块级别
  4. Built-in(内置):Python内置
python 复制代码
# 示例
x = "全局变量"

def outer():
    x = "外层函数变量"
    
    def inner():
        x = "内层函数变量"
        print(f"inner: {x}")  # 输出:内层函数变量
    
    inner()
    print(f"outer: {x}")  # 输出:外层函数变量

outer()
print(f"global: {x}")  # 输出:全局变量

4. nonlocal关键字

python 复制代码
def outer():
    count = 0
    
    def inner():
        nonlocal count  # 声明要修改外层函数的变量
        count += 1
        print(f"count: {count}")
    
    inner()  # 输出:count: 1
    inner()  # 输出:count: 2
    inner()  # 输出:count: 3
    
    print(f"final count: {count}")  # 输出:final count: 3

outer()

四、匿名函数和高阶函数

1. lambda匿名函数

lambda函数是一种简洁的匿名函数,用于简单的操作。

python 复制代码
# 基本语法
# lambda 参数: 表达式

# 普通函数
def square(x):
    return x ** 2

# lambda函数
square_lambda = lambda x: x ** 2

print(square(5))        # 输出:25
print(square_lambda(5))  # 输出:25

# 多个参数
add = lambda a, b: a + b
print(add(3, 5))  # 输出:8

# 带默认参数
greet = lambda name, message="你好": f"{name},{message}"
print(greet("小明"))  # 输出:小明,你好

2. lambda的应用场景

排序

python 复制代码
# 按字典的某个键排序
students = [
    {"name": "张三", "score": 85},
    {"name": "李四", "score": 92},
    {"name": "王五", "score": 78}
]

# 使用lambda作为排序依据
sorted_students = sorted(students, key=lambda x: x["score"], reverse=True)

for student in sorted_students:
    print(f"{student['name']}: {student['score']}")

输出:

makefile 复制代码
李四: 92
张三: 85
王五: 78

与map()配合使用

python 复制代码
# map()函数:对序列中的每个元素应用函数
numbers = [1, 2, 3, 4, 5]

# 使用lambda计算平方
squares = list(map(lambda x: x ** 2, numbers))
print(squares)  # 输出:[1, 4, 9, 16, 25]

# 等价于列表推导式
squares = [x ** 2 for x in numbers]
print(squares)  # 输出:[1, 4, 9, 16, 25]

与filter()配合使用

python 复制代码
# filter()函数:过滤序列
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 使用lambda过滤偶数
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens)  # 输出:[2, 4, 6, 8, 10]

# 等价于列表推导式
evens = [x for x in numbers if x % 2 == 0]
print(evens)  # 输出:[2, 4, 6, 8, 10]

3. 高阶函数

高阶函数是指接受函数作为参数或返回函数的函数。

map()函数

python 复制代码
# 对列表中每个元素应用函数
numbers = [1, 2, 3, 4, 5]

# 计算每个数的平方
squares = list(map(lambda x: x ** 2, numbers))
print(squares)  # 输出:[1, 4, 9, 16, 25]

# 多个列表
list1 = [1, 2, 3]
list2 = [10, 20, 30]
result = list(map(lambda x, y: x + y, list1, list2))
print(result)  # 输出:[11, 22, 33]

filter()函数

python 复制代码
# 过滤序列
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 过滤出大于5的数
greater_than_5 = list(filter(lambda x: x > 5, numbers))
print(greater_than_5)  # 输出:[6, 7, 8, 9, 10]

# 过滤出字符串中的数字
strings = ["1", "2", "a", "3", "b", "4"]
numbers = list(filter(lambda x: x.isdigit(), strings))
print(numbers)  # 输出:['1', '2', '3', '4']

reduce()函数

python 复制代码
from functools import reduce

# 对序列中的元素进行累积操作
numbers = [1, 2, 3, 4, 5]

# 计算乘积
product = reduce(lambda x, y: x * y, numbers)
print(product)  # 输出:120 (1*2*3*4*5)

# 计算和
total = reduce(lambda x, y: x + y, numbers)
print(total)  # 输出:15 (1+2+3+4+5)

# 带初始值
total = reduce(lambda x, y: x + y, numbers, 10)
print(total)  # 输出:25 (10+1+2+3+4+5)

sorted()函数

python 复制代码
# 自定义排序
students = [
    {"name": "张三", "score": 85, "age": 20},
    {"name": "李四", "score": 92, "age": 21},
    {"name": "王五", "score": 78, "age": 19}
]

# 按成绩排序
by_score = sorted(students, key=lambda x: x["score"], reverse=True)
print("按成绩排序:")
for s in by_score:
    print(f"{s['name']}: {s['score']}")

# 按年龄排序
by_age = sorted(students, key=lambda x: x["age"])
print("\n按年龄排序:")
for s in by_age:
    print(f"{s['name']}: {s['age']}")

4. 函数作为返回值

python 复制代码
# 返回函数的函数
def create_multiplier(n):
    """创建一个乘法器"""
    return lambda x: x * n

# 创建不同的乘法器
double = create_multiplier(2)
triple = create_multiplier(3)

print(double(5))   # 输出:10
print(triple(5))   # 输出:15
print(double(10))  # 输出:20

五、递归函数

1. 什么是递归

递归是指函数调用自身的过程。递归必须有终止条件,否则会无限循环。

python 复制代码
# 计算阶乘
def factorial(n):
    """计算n的阶乘"""
    # 终止条件
    if n == 0 or n == 1:
        return 1
    # 递归调用
    return n * factorial(n - 1)

# 调用
print(factorial(5))  # 输出:120 (5*4*3*2*1)
print(factorial(10))  # 输出:3628800

2. 递归的应用

斐波那契数列

python 复制代码
def fibonacci(n):
    """计算第n个斐波那契数"""
    if n <= 1:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

# 打印前10个斐波那契数
for i in range(10):
    print(fibonacci(i), end=" ")
# 输出:0 1 1 2 3 5 8 13 21 34

汉诺塔

python 复制代码
def hanoi(n, source="A", target="C", auxiliary="B"):
    """汉诺塔问题"""
    if n == 1:
        print(f"移动盘子 1 从 {source} 到 {target}")
        return
    
    # 将n-1个盘子从source移动到auxiliary
    hanoi(n - 1, source, auxiliary, target)
    
    # 移动第n个盘子
    print(f"移动盘子 {n} 从 {source} 到 {target}")
    
    # 将n-1个盘子从auxiliary移动到target
    hanoi(n - 1, auxiliary, target, source)

# 3个盘子
hanoi(3)

3. 递归的注意事项

python 复制代码
# ⚠️ 递归深度限制
import sys
print(f"最大递归深度:{sys.getrecursionlimit()}")  # 通常是1000

# 递归过深会报错
# def infinite_recursion():
#     infinite_recursion()
# infinite_recursion()  # RecursionError: maximum recursion depth exceeded

# 使用缓存优化递归
from functools import lru_cache

@lru_cache(maxsize=None)
def fibonacci_cached(n):
    if n <= 1:
        return n
    return fibonacci_cached(n - 1) + fibonacci_cached(n - 2)

# 使用缓存后,计算速度大大提升
print(fibonacci_cached(100))  # 可以快速计算

六、实战练习

练习1:计算器函数

python 复制代码
def calculator(num1, operator, num2):
    """简单计算器"""
    if operator == "+":
        return num1 + num2
    elif operator == "-":
        return num1 - num2
    elif operator == "*":
        return num1 * num2
    elif operator == "/":
        if num2 == 0:
            return "错误:除数不能为0"
        return num1 / num2
    else:
        return "错误:不支持的运算符"

# 测试
print(calculator(10, "+", 5))   # 输出:15
print(calculator(10, "-", 5))   # 输出:5
print(calculator(10, "*", 5))   # 输出:50
print(calculator(10, "/", 5))   # 输出:2.0
print(calculator(10, "/", 0))   # 输出:错误:除数不能为0

练习2:统计函数

python 复制代码
def analyze_numbers(numbers):
    """分析数字列表,返回统计信息"""
    if not numbers:
        return None
    
    return {
        "count": len(numbers),
        "sum": sum(numbers),
        "average": sum(numbers) / len(numbers),
        "max": max(numbers),
        "min": min(numbers)
    }

# 测试
data = [10, 20, 30, 40, 50]
stats = analyze_numbers(data)

print(f"数据:{data}")
print(f"数量:{stats['count']}")
print(f"总和:{stats['sum']}")
print(f"平均值:{stats['average']}")
print(f"最大值:{stats['max']}")
print(f"最小值:{stats['min']}")

练习3:装饰器入门

python 复制代码
import time

def timer_decorator(func):
    """计时装饰器"""
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"{func.__name__} 执行时间:{end_time - start_time:.4f}秒")
        return result
    return wrapper

@timer_decorator
def slow_function():
    """模拟耗时操作"""
    time.sleep(1)
    print("函数执行完成")

# 调用
slow_function()

练习4:函数式编程

python 复制代码
from functools import reduce

# 数据处理管道
data = [
    {"name": "张三", "score": 85, "age": 20},
    {"name": "李四", "score": 92, "age": 21},
    {"name": "王五", "score": 78, "age": 19},
    {"name": "赵六", "score": 95, "age": 22},
    {"name": "钱七", "score": 88, "age": 20}
]

# 1. 过滤出成绩大于80的学生
high_scorers = list(filter(lambda x: x["score"] > 80, data))
print("成绩大于80的学生:")
for s in high_scorers:
    print(f"  {s['name']}: {s['score']}")

# 2. 提取所有成绩
scores = list(map(lambda x: x["score"], data))
print(f"\n所有成绩:{scores}")

# 3. 计算平均分
average = reduce(lambda x, y: x + y, scores) / len(scores)
print(f"平均分:{average:.1f}")

# 4. 按成绩排序
sorted_data = sorted(data, key=lambda x: x["score"], reverse=True)
print("\n按成绩排序:")
for i, s in enumerate(sorted_data, 1):
    print(f"  {i}. {s['name']}: {s['score']}")

七、常见问题与排错

问题1:忘记return语句

原因 :函数没有返回值 解决方法:添加return语句

python 复制代码
# ❌ 错误
def add(a, b):
    result = a + b
    # 忘记return

sum_result = add(3, 5)
print(sum_result)  # 输出:None

# ✅ 正确
def add(a, b):
    result = a + b
    return result

sum_result = add(3, 5)
print(sum_result)  # 输出:8

问题2:默认参数是可变对象

原因 :默认参数在函数定义时只创建一次 解决方法:使用None作为默认值

python 复制代码
# ❌ 错误
def append_to(element, lst=[]):
    lst.append(element)
    return lst

print(append_to(1))  # 输出:[1]
print(append_to(2))  # 输出:[1, 2](不是[2]!)

# ✅ 正确
def append_to(element, lst=None):
    if lst is None:
        lst = []
    lst.append(element)
    return lst

print(append_to(1))  # 输出:[1]
print(append_to(2))  # 输出:[2]

问题3:作用域问题

原因 :在函数内部修改全局变量没有使用global 解决方法:使用global声明

python 复制代码
count = 0

# ❌ 错误
def increment():
    count += 1  # UnboundLocalError

# ✅ 正确
def increment():
    global count
    count += 1

increment()
print(count)  # 输出:1

八、本篇小结

今天,我们完成了以下学习内容:

  1. 函数的定义和调用:

    • 使用 def 定义函数
    • 函数名要见名知意
    • 使用文档字符串说明函数
  2. 参数和返回值:

    • 位置参数、关键字参数
    • 默认参数、可变参数(*args、**kwargs)
    • return返回值
  3. 变量的作用域:

    • 局部变量和全局变量
    • LEGB规则
    • global和nonlocal关键字
  4. 匿名函数和高阶函数:

    • lambda匿名函数
    • map()、filter()、reduce()
    • sorted()自定义排序
  5. 递归函数:

    • 递归的概念
    • 递归的应用
    • 递归深度限制

请记住这三个结论:

  1. 函数是代码复用的基础:把重复的代码封装成函数
  2. 参数传递要灵活:合理使用位置参数、关键字参数、默认参数
  3. 高阶函数很强大:map、filter、reduce可以简化代码

下期预告

在第9期中,我们将学习Python的文件操作,包括:

  • 文件的打开和关闭
  • 读写文本文件
  • 读写CSV和JSON文件
  • 文件路径处理

敬请期待!


✍坚持原创,求关注,点赞,收藏

相关推荐
码事漫谈1 小时前
你让 AI “尽力做”,它反而可能做不好
后端
孟健2 小时前
LLM API 安全防线:从 OpenCode 盗刷事件到 LiteLLM 多模型容灾实操
人工智能·后端·架构
程序员cxuan2 小时前
我用 WorkBuddy + ima 把公众号文章做成了知识库。
人工智能·后端·程序员
程序员cxuan2 小时前
玩了一天 REA,我号快没了
人工智能·后端·程序员
卷无止境3 小时前
暗流与破局:AI时代的代码安全审查工具全景指南
后端·python
smallyoung3 小时前
告别N+1:Spring Boot 一键数据翻译框架体系
java·后端·架构
程序猿DD3 小时前
OctaFuse Gateway 2.16.0:新增 ElevenLabs 的支持、厂商原生端点重构、后台管理优化
后端
掘金者阿豪3 小时前
列存储适不适合订单报表?从一条慢 SQL 聊聊金仓 KES 的使用边界
后端
武子康3 小时前
150毫秒和2400毫秒:同一 Agent 任务的两种延迟
人工智能·后端·agent