第5天-函数

2023/6/15

# 第5天-Python函数

今天我们来学习Python中最重要的概念之一:函数(Function)。函数是编程的核心,掌握了函数,你就掌握了编程的精髓!

# 函数是什么?

想象一下生活中的场景:

  • 🍳 做饭:你给厨师食材(输入),厨师做出美食(输出)
  • 🧮 计算器:你输入数字和运算符,计算器给出结果
  • 🏭 工厂:输入原材料,输出成品

Python的函数就是这样的"加工厂"!

函数定义:函数是一段可重复使用的代码块,它接收输入(参数),执行特定任务,并可能返回结果。


# 一、函数的基本语法

# 1.1 定义函数的语法

def 函数名(参数1, 参数2, ...):
    """函数说明文档(可选)"""
    # 函数体:要执行的代码
    return 返回值  # 可选

语法要点

  • def:定义函数的关键字
  • 函数名:遵循变量命名规则
  • 参数:函数的输入,可以有0个或多个
  • ::不要忘记冒号
  • 缩进:函数体必须缩进
  • return:返回结果(可选)

# 1.2 最简单的函数

# 定义一个最简单的函数
def say_hello():
    print("你好,欢迎学习Python!")

# 调用函数
say_hello()  # 输出:你好,欢迎学习Python!

小白提示:定义函数只是"制作模板",调用函数才是"使用模板"!

# 1.3 带参数的函数

# 带一个参数的函数
def greet(name):
    print(f"你好,{name}!")

# 调用函数时传入参数
greet("小明")    # 输出:你好,小明!
greet("Alice")  # 输出:你好,Alice!

# 带多个参数的函数
def introduce(name, age, city):
    print(f"我叫{name},今年{age}岁,来自{city}")

introduce("张三", 25, "北京")
# 输出:我叫张三,今年25岁,来自北京

# 1.4 带返回值的函数

# 计算两个数的和
def add(a, b):
    result = a + b
    return result  # 返回计算结果

# 使用返回值
sum_result = add(3, 5)
print(f"3 + 5 = {sum_result}")  # 输出:3 + 5 = 8

# 可以直接在表达式中使用
print(f"10 + 20 = {add(10, 20)}")  # 输出:10 + 20 = 30

重要概念

  • return的函数会返回值,可以赋值给变量
  • 没有return的函数返回None
  • return后面的代码不会执行

# 二、函数参数详解

# 2.1 位置参数(必需参数)

位置参数是最基本的参数类型,调用时必须按顺序传入:

def calculate_rectangle_area(length, width):
    """计算矩形面积"""
    area = length * width
    return area

# 按位置传参:第一个是length,第二个是width
area1 = calculate_rectangle_area(5, 3)  # length=5, width=3
print(f"矩形面积:{area1}")  # 输出:矩形面积:15

# 位置不能搞错!
area2 = calculate_rectangle_area(3, 5)  # length=3, width=5
print(f"矩形面积:{area2}")  # 输出:矩形面积:15(结果相同,但概念不同)

# 2.2 关键字参数

可以通过参数名指定值,不用考虑顺序:

def create_user_profile(name, age, city, job):
    return f"姓名:{name},年龄:{age},城市:{city},职业:{job}"

# 使用关键字参数,顺序可以任意
profile1 = create_user_profile(name="张三", age=28, city="上海", job="程序员")
profile2 = create_user_profile(job="设计师", city="深圳", name="李四", age=26)

print(profile1)
print(profile2)

# 位置参数和关键字参数可以混用,但位置参数必须在前面
profile3 = create_user_profile("王五", 30, city="广州", job="老师")
print(profile3)

# 2.3 默认参数

为参数设置默认值,调用时可以不传该参数:

def greet_user(name, greeting="你好", punctuation="!"):
    return f"{greeting}{name}{punctuation}"

# 使用默认参数
print(greet_user("小明"))  # 输出:你好,小明!

# 覆盖部分默认参数
print(greet_user("小红", "早上好"))  # 输出:早上好,小红!

# 覆盖所有默认参数
print(greet_user("小李", "晚安", "。"))  # 输出:晚安,小李。

# 使用关键字参数跳过某些默认参数
print(greet_user("小王", punctuation="???"))  # 输出:你好,小王???

默认参数注意事项

  • 默认参数必须放在位置参数后面
  • 不要使用可变对象(如列表、字典)作为默认参数

# 2.4 可变参数(*args)

当不知道会传入多少个参数时,使用*args

def calculate_sum(*numbers):
    """计算任意数量数字的和"""
    total = 0
    for num in numbers:
        total += num
    return total

# 传入不同数量的参数
print(calculate_sum(1, 2, 3))           # 输出:6
print(calculate_sum(10, 20, 30, 40))    # 输出:100
print(calculate_sum(5))                 # 输出:5
print(calculate_sum())                  # 输出:0

# 更简洁的写法
def calculate_sum_v2(*numbers):
    return sum(numbers)

print(calculate_sum_v2(1, 2, 3, 4, 5))  # 输出:15

# 2.5 关键字可变参数(**kwargs)

当需要接收任意数量的关键字参数时,使用**kwargs

def create_student_info(name, **other_info):
    """创建学生信息"""
    info = f"学生姓名:{name}\n"
    for key, value in other_info.items():
        info += f"{key}{value}\n"
    return info

# 传入不同的关键字参数
student1 = create_student_info(
    "张三", 
    age=20, 
    major="计算机科学", 
    grade="大二",
    gpa=3.8
)
print(student1)

student2 = create_student_info(
    "李四",
    age=19,
    hobby="篮球",
    hometown="北京"
)
print(student2)

# 2.6 参数组合使用

def complex_function(required_arg, default_arg="默认值", *args, **kwargs):
    """演示各种参数类型的组合使用"""
    print(f"必需参数:{required_arg}")
    print(f"默认参数:{default_arg}")
    print(f"可变参数:{args}")
    print(f"关键字参数:{kwargs}")
    print("-" * 30)

# 各种调用方式
complex_function("必须的")
complex_function("必须的", "修改默认值")
complex_function("必须的", "修改默认值", 1, 2, 3)
complex_function("必须的", "修改默认值", 1, 2, 3, name="张三", age=25)

# 三、函数的返回值

# 3.1 单个返回值

def get_circle_area(radius):
    """计算圆的面积"""
    import math
    area = math.pi * radius ** 2
    return area

area = get_circle_area(5)
print(f"半径为5的圆的面积:{area:.2f}")  # 输出:半径为5的圆的面积:78.54

# 3.2 多个返回值

def get_rectangle_info(length, width):
    """计算矩形的面积和周长"""
    area = length * width
    perimeter = 2 * (length + width)
    return area, perimeter  # 返回元组

# 接收多个返回值
area, perimeter = get_rectangle_info(5, 3)
print(f"面积:{area},周长:{perimeter}")

# 也可以作为元组接收
result = get_rectangle_info(4, 6)
print(f"结果元组:{result}")  # 输出:结果元组:(24, 20)
print(f"面积:{result[0]},周长:{result[1]}")

# 3.3 条件返回

def check_grade(score):
    """根据分数返回等级"""
    if score >= 90:
        return "优秀"
    elif score >= 80:
        return "良好"
    elif score >= 70:
        return "中等"
    elif score >= 60:
        return "及格"
    else:
        return "不及格"

# 测试不同分数
scores = [95, 85, 75, 65, 55]
for score in scores:
    grade = check_grade(score)
    print(f"分数{score}{grade}")

# 3.4 提前返回

def divide_numbers(a, b):
    """安全的除法运算"""
    if b == 0:
        print("错误:除数不能为0")
        return None  # 提前返回,避免错误
    
    result = a / b
    return result

# 测试
print(divide_numbers(10, 2))  # 输出:5.0
print(divide_numbers(10, 0))  # 输出:错误:除数不能为0,然后是None

# 四、函数的作用域

# 4.1 局部变量 vs 全局变量

# 全局变量
global_var = "我是全局变量"
counter = 0

def demo_scope():
    # 局部变量
    local_var = "我是局部变量"
    print(f"函数内部访问全局变量:{global_var}")
    print(f"函数内部的局部变量:{local_var}")

demo_scope()
print(f"函数外部访问全局变量:{global_var}")
# print(local_var)  # 错误!无法在函数外访问局部变量

# 4.2 修改全局变量

counter = 0  # 全局变量

def increment_counter():
    global counter  # 声明要修改全局变量
    counter += 1
    print(f"计数器增加到:{counter}")

def show_counter():
    print(f"当前计数器值:{counter}")

show_counter()        # 输出:当前计数器值:0
increment_counter()   # 输出:计数器增加到:1
increment_counter()   # 输出:计数器增加到:2
show_counter()        # 输出:当前计数器值:2

# 4.3 变量查找顺序(LEGB规则)

x = "全局变量"

def outer_function():
    x = "外层函数变量"
    
    def inner_function():
        x = "内层函数变量"
        print(f"内层函数中的x:{x}")
    
    inner_function()
    print(f"外层函数中的x:{x}")

outer_function()
print(f"全局作用域中的x:{x}")

# 输出:
# 内层函数中的x:内层函数变量
# 外层函数中的x:外层函数变量
# 全局作用域中的x:全局变量

LEGB规则:Python按照 Local → Enclosing → Global → Built-in 的顺序查找变量


# 五、Lambda表达式(匿名函数)

# 5.1 什么是Lambda表达式?

Lambda表达式是一种创建简单函数的快捷方式,适合一行就能完成的简单操作。

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

# Lambda表达式(匿名函数)
square_lambda = lambda x: x ** 2

# 两者效果相同
print(square(5))        # 输出:25
print(square_lambda(5)) # 输出:25

# 5.2 Lambda语法

# 基本语法:lambda 参数: 表达式

# 单个参数
double = lambda x: x * 2
print(double(4))  # 输出:8

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

# 无参数
get_pi = lambda: 3.14159
print(get_pi())  # 输出:3.14159

# 条件表达式
max_value = lambda a, b: a if a > b else b
print(max_value(10, 20))  # 输出:20

# 5.3 Lambda的实际应用

# 1. 与内置函数配合使用
numbers = [1, 2, 3, 4, 5]

# 使用map():对每个元素应用函数
squares = list(map(lambda x: x**2, numbers))
print(f"平方:{squares}")  # 输出:平方:[1, 4, 9, 16, 25]

# 使用filter():过滤元素
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(f"偶数:{even_numbers}")  # 输出:偶数:[2, 4]

# 使用sorted():自定义排序
students = [('Alice', 85), ('Bob', 90), ('Charlie', 78)]
sorted_by_score = sorted(students, key=lambda student: student[1])
print(f"按分数排序:{sorted_by_score}")
# 输出:按分数排序:[('Charlie', 78), ('Alice', 85), ('Bob', 90)]

# 5.4 Lambda vs 普通函数

特性 Lambda表达式 普通函数
语法 简洁,一行 完整,多行
命名 匿名 有名称
复杂度 只能是表达式 可以包含语句
调试 难以调试 容易调试
用途 简单操作 复杂逻辑

# 六、高级函数概念

# 6.1 递归函数

递归是函数调用自己的编程技巧,适合解决可以分解为相似子问题的问题。

# 经典例子:计算阶乘
def factorial(n):
    """计算n的阶乘:n! = n × (n-1) × ... × 1"""
    # 基础情况(递归终止条件)
    if n == 0 or n == 1:
        return 1
    # 递归情况
    else:
        return n * factorial(n - 1)

# 测试
for i in range(6):
    print(f"{i}! = {factorial(i)}")

# 输出:
# 0! = 1
# 1! = 1
# 2! = 2
# 3! = 6
# 4! = 24
# 5! = 120
# 斐波那契数列
def fibonacci(n):
    """计算斐波那契数列的第n项"""
    if n <= 1:
        return n
    else:
        return fibonacci(n-1) + fibonacci(n-2)

# 打印前10项斐波那契数列
print("斐波那契数列前10项:")
for i in range(10):
    print(f"F({i}) = {fibonacci(i)}")

递归注意事项

  • 必须有终止条件(基础情况)
  • 递归调用必须向终止条件靠近
  • 递归深度不能太大(Python默认限制1000层)

# 6.2 嵌套函数

在函数内部定义另一个函数:

def outer_function(x):
    """外层函数"""
    
    def inner_function(y):
        """内层函数"""
        return y * 2
    
    # 在外层函数中调用内层函数
    result = inner_function(x) + 10
    return result

print(outer_function(5))  # 输出:20 (5*2+10)

# inner_function(5)  # 错误!内层函数在外部不可访问

# 6.3 闭包(Closure)

内层函数引用外层函数的变量:

def create_multiplier(factor):
    """创建一个乘法器函数"""
    
    def multiplier(number):
        return number * factor  # 引用外层函数的参数
    
    return multiplier  # 返回内层函数

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

print(double(5))   # 输出:10 (5 * 2)
print(triple(5))   # 输出:15 (5 * 3)

# 每个乘法器都"记住"了自己的factor值
print(double(10))  # 输出:20
print(triple(10))  # 输出:30

# 6.4 装饰器基础

装饰器是一种特殊的函数,用来修改或增强其他函数的功能:

def timer_decorator(func):
    """计时装饰器"""
    import time
    
    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():
    """一个慢函数"""
    import time
    time.sleep(1)  # 模拟耗时操作
    return "任务完成"

# 调用被装饰的函数
result = slow_function()
print(result)
# 输出:
# 函数 slow_function 执行时间:1.0012秒
# 任务完成

# 七、函数式编程基础

# 7.1 高阶函数

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

def apply_operation(numbers, operation):
    """对数字列表应用指定操作"""
    result = []
    for num in numbers:
        result.append(operation(num))
    return result

# 定义一些操作函数
def square(x):
    return x ** 2

def cube(x):
    return x ** 3

numbers = [1, 2, 3, 4, 5]

# 使用不同的操作
squares = apply_operation(numbers, square)
cubes = apply_operation(numbers, cube)

print(f"原数字:{numbers}")
print(f"平方:{squares}")
print(f"立方:{cubes}")

# 7.2 常用内置高阶函数

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# map():对每个元素应用函数
squares = list(map(lambda x: x**2, numbers))
print(f"平方:{squares}")

# filter():过滤元素
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(f"偶数:{even_numbers}")

# reduce():累积操作
from functools import reduce
sum_all = reduce(lambda x, y: x + y, numbers)
print(f"所有数字的和:{sum_all}")

# any():任意一个为True
has_even = any(x % 2 == 0 for x in numbers)
print(f"是否包含偶数:{has_even}")

# all():所有都为True
all_positive = all(x > 0 for x in numbers)
print(f"是否都是正数:{all_positive}")

# 八、实战练习

# 8.1 练习1:温度转换器

def celsius_to_fahrenheit(celsius):
    """摄氏度转华氏度"""
    fahrenheit = (celsius * 9/5) + 32
    return fahrenheit

def fahrenheit_to_celsius(fahrenheit):
    """华氏度转摄氏度"""
    celsius = (fahrenheit - 32) * 5/9
    return celsius

def temperature_converter():
    """温度转换主程序"""
    print("温度转换器")
    print("1. 摄氏度转华氏度")
    print("2. 华氏度转摄氏度")
    
    choice = input("请选择转换类型 (1/2): ")
    
    if choice == '1':
        celsius = float(input("请输入摄氏度: "))
        fahrenheit = celsius_to_fahrenheit(celsius)
        print(f"{celsius}°C = {fahrenheit:.2f}°F")
    elif choice == '2':
        fahrenheit = float(input("请输入华氏度: "))
        celsius = fahrenheit_to_celsius(fahrenheit)
        print(f"{fahrenheit}°F = {celsius:.2f}°C")
    else:
        print("无效选择")

# 运行程序
# temperature_converter()

# 8.2 练习2:学生成绩管理

def calculate_average(scores):
    """计算平均分"""
    if not scores:
        return 0
    return sum(scores) / len(scores)

def get_grade(score):
    """根据分数获取等级"""
    if score >= 90:
        return 'A'
    elif score >= 80:
        return 'B'
    elif score >= 70:
        return 'C'
    elif score >= 60:
        return 'D'
    else:
        return 'F'

def analyze_scores(student_scores):
    """分析学生成绩"""
    print("学生成绩分析报告")
    print("=" * 30)
    
    for name, scores in student_scores.items():
        avg_score = calculate_average(scores)
        grade = get_grade(avg_score)
        
        print(f"学生:{name}")
        print(f"  各科成绩:{scores}")
        print(f"  平均分:{avg_score:.2f}")
        print(f"  等级:{grade}")
        print("-" * 20)

# 测试数据
student_data = {
    "张三": [85, 92, 78, 96],
    "李四": [76, 88, 82, 79],
    "王五": [95, 87, 91, 93]
}

analyze_scores(student_data)

# 8.3 练习3:简单计算器

def add(a, b):
    """加法"""
    return a + b

def subtract(a, b):
    """减法"""
    return a - b

def multiply(a, b):
    """乘法"""
    return a * b

def divide(a, b):
    """除法"""
    if b == 0:
        return "错误:除数不能为0"
    return a / b

def calculator():
    """简单计算器"""
    operations = {
        '+': add,
        '-': subtract,
        '*': multiply,
        '/': divide
    }
    
    print("简单计算器")
    print("支持的操作:+, -, *, /")
    print("输入 'quit' 退出")
    
    while True:
        try:
            expression = input("请输入计算表达式 (如: 5 + 3): ")
            
            if expression.lower() == 'quit':
                print("再见!")
                break
            
            # 解析输入
            parts = expression.split()
            if len(parts) != 3:
                print("格式错误,请使用格式:数字 操作符 数字")
                continue
            
            num1, operator, num2 = parts
            num1, num2 = float(num1), float(num2)
            
            if operator in operations:
                result = operations[operator](num1, num2)
                print(f"结果:{num1} {operator} {num2} = {result}")
            else:
                print("不支持的操作符")
                
        except ValueError:
            print("输入错误,请输入有效的数字")
        except Exception as e:
            print(f"发生错误:{e}")

# 运行计算器
# calculator()

# 九、函数最佳实践

# 9.1 函数设计原则

# ✅ 好的函数设计
def calculate_circle_area(radius):
    """计算圆的面积
    
    Args:
        radius (float): 圆的半径
    
    Returns:
        float: 圆的面积
    
    Raises:
        ValueError: 当半径为负数时
    """
    if radius < 0:
        raise ValueError("半径不能为负数")
    
    import math
    return math.pi * radius ** 2

# ❌ 不好的函数设计
def calc(r):
    # 没有文档说明
    # 变量名不清晰
    # 没有错误处理
    return 3.14 * r * r

# 9.2 函数命名规范

# ✅ 好的函数命名
def get_user_age(user_id):
    """获取用户年龄"""
    pass

def is_valid_email(email):
    """检查邮箱是否有效"""
    pass

def calculate_total_price(items):
    """计算总价格"""
    pass

# ❌ 不好的函数命名
def func1(x):  # 名称不明确
    pass

def getData():  # 不符合Python命名规范
    pass

def do_everything():  # 功能不单一
    pass

# 9.3 函数长度和复杂度

# ✅ 单一职责,简洁明了
def validate_password(password):
    """验证密码强度"""
    if len(password) < 8:
        return False, "密码长度至少8位"
    
    if not any(c.isupper() for c in password):
        return False, "密码必须包含大写字母"
    
    if not any(c.islower() for c in password):
        return False, "密码必须包含小写字母"
    
    if not any(c.isdigit() for c in password):
        return False, "密码必须包含数字"
    
    return True, "密码强度合格"

# 测试
passwords = ["123456", "Password", "Password123"]
for pwd in passwords:
    is_valid, message = validate_password(pwd)
    print(f"密码 '{pwd}': {message}")

# 十、总结

# 10.1 函数知识点总结

概念 说明 示例
函数定义 使用def关键字 def func_name():
参数类型 位置、关键字、默认、可变 def func(a, b=1, *args, **kwargs):
返回值 使用return返回结果 return result
作用域 局部变量vs全局变量 global variable_name
Lambda 匿名函数,简洁语法 lambda x: x * 2
递归 函数调用自己 阶乘、斐波那契
装饰器 增强函数功能 @decorator

# 10.2 函数使用建议

  1. 单一职责:每个函数只做一件事
  2. 命名清晰:函数名要能表达其功能
  3. 参数合理:参数不要太多(建议不超过5个)
  4. 文档完整:写好函数说明文档
  5. 错误处理:考虑异常情况
  6. 测试充分:编写测试用例

# 10.3 下一步学习方向

掌握了函数后,你可以继续学习:

  • 模块和包:代码组织和复用
  • 面向对象编程:类和对象
  • 异常处理:错误处理机制
  • 文件操作:读写文件
  • 高级特性:生成器、装饰器等

恭喜你!函数是编程的核心,掌握了函数,你已经具备了编写复杂程序的基础能力。继续加油!🚀


练习建议

  1. 尝试重写之前学过的代码,使用函数来组织
  2. 编写一些实用的小工具函数
  3. 练习使用不同类型的参数
  4. 尝试编写递归函数解决问题
  5. 学会阅读和使用他人编写的函数