来源:04_函数 hm_01~hm_06 + Python官方文档 + 网络资料整理


一、详细讲解

1.1 核心概念

函数定义与调用是Python编程中的核心知识点,也是代码模块化、复用和组织的基石。在本课程的第10天,我们将系统学习函数的基本概念、定义语法、参数传递、返回值处理,以及文档字符串的编写。

函数是一段组织好的、可重复使用的、用来实现单一或相关联功能的代码块。在Python中,函数不仅是一种代码复用的手段,更是实现面向过程编程模块化设计的基础。通过函数,我们可以将复杂的问题分解为多个小问题,使程序结构更加清晰、易于维护。

1.2 函数基础

1.2.1 为什么要使用函数

# 不使用函数:代码重复
print("=== 计算圆的面积 ===")
radius = 5
area = 3.14159 * radius ** 2
print(f"半径{radius}的圆面积: {area}")

radius = 10
area = 3.14159 * radius ** 2
print(f"半径{radius}的圆面积: {area}")

radius = 15
area = 3.14159 * radius ** 2
print(f"半径{radius}的圆面积: {area}")

# 使用函数:代码复用
def calculate_circle_area(radius):
    """计算圆的面积"""
    area = 3.14159 * radius ** 2
    return area

print("\n=== 使用函数 ===")
for r in [5, 10, 15]:
    print(f"半径{r}的圆面积: {calculate_circle_area(r)}")

1.2.2 函数定义的基本语法

# 函数定义的基本结构
def 函数名(参数1, 参数2, ...):
    """文档字符串(可选)"""
    # 函数体
    return 返回值  # 可选

1.3 函数的定义与调用

1.3.1 无参函数

# 无参函数:不需要输入参数
def greet():
    """简单的问候函数"""
    print("Hello, World!")
    print("Welcome to Python!")

# 调用函数
greet()

# 可以多次调用
greet()
greet()

1.3.2 有参函数

# 有参函数:需要输入参数
def greet_person(name):
    """向指定的人打招呼"""
    print(f"Hello, {name}!")
    print(f"Welcome, {name}!")

# 调用函数,传入参数
greet_person("Alice")
print()
greet_person("Bob")

1.3.3 多参数函数

# 多个参数的函数
def calculate_rectangle(length, width):
    """计算矩形的面积和周长"""
    area = length * width
    perimeter = 2 * (length + width)
    return area, perimeter

# 调用函数
result = calculate_rectangle(5, 3)
print(f"面积: {result[0]}, 周长: {result[1]}")

# 使用解包
area, perimeter = calculate_rectangle(5, 3)
print(f"面积: {area}")
print(f"周长: {perimeter}")

1.4 参数详解

1.4.1 位置参数

# 位置参数:按顺序传递
def introduce(name, age, city):
    """自我介绍"""
    print(f"我叫{name},今年{age}岁,来自{city}。")

# 调用时按位置传递
introduce("张三", 25, "北京")
introduce("李四", 30, "上海")
introduce("王五", 28, "广州")

# 位置参数必须传递,且顺序不能错
# introduce("赵六")  # 错误:缺少参数
# introduce(25, "钱七", "深圳")  # 错误:类型不匹配

1.4.2 关键字参数

# 关键字参数:按名称传递
def introduce(name, age, city):
    """自我介绍"""
    print(f"我叫{name},今年{age}岁,来自{city}。")

# 使用关键字参数,顺序可以改变
introduce(age=30, name="张三", city="北京")

# 混合使用:位置参数必须在关键字参数前面
introduce("李四", city="上海", age=28)

1.4.3 默认参数

# 默认参数:给参数设置默认值
def greet(name, greeting="Hello"):
    """带默认参数的问候函数"""
    print(f"{greeting}, {name}!")

# 调用时可以不指定默认参数
greet("Alice")  # Hello, Alice!
greet("Bob", "Hi")  # Hi, Bob!

# 默认参数必须在位置参数后面
# def greet(greeting="Hello", name):  # 错误!
# 默认参数可以是有副作用的可变对象(需要注意)

1.4.4 默认参数的最佳实践

# 避免使用可变对象作为默认参数
# 错误示例
def add_to_list(item, target_list=[]):  # 危险!
    target_list.append(item)
    return target_list

print(add_to_list(1))  # [1]
print(add_to_list(2))  # [1, 2] - 不是 [2]!

# 正确示例
def add_to_list(item, target_list=None):  # 正确
    if target_list is None:
        target_list = []
    target_list.append(item)
    return target_list

print(add_to_list(1))  # [1]
print(add_to_list(2))  # [2]

1.5 返回值详解

1.5.1 无返回值函数

# 没有return语句的函数,默认返回None
def print_sum(a, b):
    """打印两个数的和,不返回值"""
    print(f"{a} + {b} = {a + b}")

result = print_sum(3, 5)
print(f"返回值: {result}")  # None

# 使用return单独返回None
def check_positive(n):
    """检查是否是正数"""
    if n > 0:
        return True
    else:
        return None

print(check_positive(5))   # True
print(check_positive(-1))  # None

1.5.2 单返回值

# 返回单个值
def square(n):
    """返回n的平方"""
    return n ** 2

result = square(5)
print(f"5的平方: {result}")  # 25

1.5.3 多返回值

# 返回多个值(实际是返回元组)
def calculate(a, b):
    """返回和、差、积"""
    return a + b, a - b, a * b

# 使用元组接收
result = calculate(10, 3)
print(f"结果类型: {type(result)}")
print(f"结果值: {result}")

# 使用解包接收
sum_val, diff, prod = calculate(10, 3)
print(f"和: {sum_val}, 差: {diff}, 积: {prod}")

1.5.4 提前返回

# 使用多个return语句
def absolute_value(n):
    """返回绝对值"""
    if n < 0:
        return -n
    return n

print(absolute_value(-5))  # 5
print(absolute_value(5))   # 5

# 早期返回(Early Return)模式
def process_data(data):
    """处理数据"""
    # 验证输入
    if not data:
        return None
    
    # 主处理逻辑
    result = []
    for item in data:
        if is_valid(item):
            result.append(process(item))
    
    return result

1.6 文档字符串(Docstring)

1.6.1 文档字符串的作用

文档字符串是紧跟在函数定义之后的一个字符串,用于说明函数的功能、参数、返回值、使用示例等信息。

def calculate_circle_area(radius):
    """
    计算圆的面积
    
    Parameters:
        radius (float): 圆的半径,必须为非负数
    
    Returns:
        float: 圆的面积
    
    Examples:
        >>> calculate_circle_area(5)
        78.53975
    """
    if radius < 0:
        raise ValueError("半径不能为负数")
    return 3.14159 * radius ** 2

1.6.2 文档字符串的格式

# 单行文档字符串
def greet(name):
    """向指定的人打招呼。"""
    print(f"Hello, {name}!")

# 多行文档字符串
def calculate_stats(numbers):
    """
    计算一组数字的统计信息。
    
    参数:
        numbers (list): 数字列表
    
    返回:
        dict: 包含平均值、总和、最大值、最小值的字典
    
    示例:
        >>> calculate_stats([1, 2, 3, 4, 5])
        {'avg': 3.0, 'sum': 15, 'max': 5, 'min': 1}
    """
    return {
        'avg': sum(numbers) / len(numbers),
        'sum': sum(numbers),
        'max': max(numbers),
        'min': min(numbers)
    }

# 访问文档字符串
print(calculate_stats.__doc__)
help(calculate_stats)

1.7 函数调用详解

1.7.1 函数调用的过程

# 函数调用过程分析
def add(a, b):
    """加法函数"""
    result = a + b
    return result

# 调用步骤:
# 1. 传递参数:a=3, b=5
# 2. 执行函数体:result = 8
# 3. 返回结果:return 8
# 4. 接收返回值:total = 8

total = add(3, 5)
print(f"结果: {total}")

1.7.2 嵌套调用

# 函数可以嵌套调用
def square(n):
    """返回n的平方"""
    return n ** 2

def sum_of_squares(a, b):
    """返回a的平方加b的平方"""
    return square(a) + square(b)

result = sum_of_squares(3, 4)  # 3² + 4² = 9 + 16 = 25
print(f"3² + 4² = {result}")

1.8 变量的作用域

1.8.1 局部变量与全局变量

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

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

test_scope()
# print(local_var)  # 错误!局部变量在函数外无法访问

# 在函数内修改全局变量需要使用global关键字
counter = 0

def increment():
    global counter
    counter += 1
    print(f"计数器: {counter}")

increment()
increment()
print(f"函数外计数器: {counter}")

1.8.2 LEGB规则

Python查找变量时遵循LEGB规则:

  • Local:局部作用域
  • Enclosing:闭包作用域
  • Global:全局作用域
  • Built-in:内置作用域
# LEGB规则示例
x = "全局变量"

def outer():
    x = "闭包变量"
    
    def inner():
        x = "局部变量"
        print(f"inner中的x: {x}")  # 局部变量
    
    inner()
    print(f"outer中的x: {x}")  # 闭包变量

outer()
print(f"全局中的x: {x}")  # 全局变量

1.9 实用函数示例

1.9.1 数学运算函数

# 判断质数
def is_prime(n):
    """判断n是否为质数"""
    if n < 2:
        return False
    for i in range(2, int(n ** 0.5) + 1):
        if n % i == 0:
            return False
    return True

# 打印n以内的所有质数
def print_primes(n):
    """打印n以内的所有质数"""
    primes = []
    for i in range(2, n + 1):
        if is_prime(i):
            primes.append(i)
    return primes

print(f"100以内的质数: {print_primes(100)}")

# 计算阶乘
def factorial(n):
    """计算n的阶乘"""
    if n < 0:
        raise ValueError("负数没有阶乘")
    if n <= 1:
        return 1
    result = 1
    for i in range(2, n + 1):
        result *= i
    return result

print(f"5! = {factorial(5)}")  # 120

1.9.2 字符串处理函数

# 反转字符串
def reverse_string(s):
    """反转字符串"""
    return s[::-1]

print(f"反转'abcde': {reverse_string('abcde')}")

# 判断回文
def is_palindrome(s):
    """判断是否为回文"""
    clean = ''.join(c.lower() for c in s if c.isalnum())
    return clean == clean[::-1]

print(f"'上海自来水来自海上'是回文: {is_palindrome('上海自来水来自海上')}")

# 统计单词数
def count_words(text):
    """统计单词数量"""
    words = text.split()
    return len(words)

print(f"'Hello World Python'有 {count_words('Hello World Python')} 个单词")

1.9.3 数据验证函数

# 验证邮箱格式
def is_valid_email(email):
    """简单验证邮箱格式"""
    import re
    pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$'
    return bool(re.match(pattern, email))

emails = ["test@example.com", "invalid-email", "user@domain.org"]
for email in emails:
    print(f"{email}: {is_valid_email(email)}")

# 验证数字范围
def is_in_range(value, min_val, max_val):
    """验证值是否在指定范围内"""
    return min_val <= value <= max_val

print(f"5在1-10范围内: {is_in_range(5, 1, 10)}")

1.10 最佳实践

1.10.1 函数设计原则

# 单一职责原则:每个函数只做一件事
# 不好:一个函数做多件事
def process_user(name, age, action):
    if action == "create":
        print(f"创建用户 {name}, 年龄 {age}")
    elif action == "delete":
        print(f"删除用户 {name}")
    elif action == "update":
        print(f"更新用户 {name}, 年龄 {age}")

# 好:每个函数职责单一
def create_user(name, age):
    """创建用户"""
    print(f"创建用户 {name}, 年龄 {age}")

def delete_user(name):
    """删除用户"""
    print(f"删除用户 {name}")

def update_user(name, age):
    """更新用户"""
    print(f"更新用户 {name}, 年龄 {age}")

1.10.2 参数设计建议

# 使用有意义的参数名
def calculate_area(width, height):  # 好
    return width * height

# 避免使用魔法数字
def calculate_circle_area(radius, pi=3.14159):  # 使用默认参数
    return pi * radius ** 2

1.10.3 文档字符串编写规范

# 使用docstring格式
def function_name(param1, param2):
    """
    简短描述(第一行以动词开头,句号结尾)
    
    详细描述(可选,说明函数的工作原理)
    
    Args:
        param1: 参数1的说明
        param2: 参数2的说明
    
    Returns:
        返回值的说明
    
    Raises:
        ValueError: 何时抛出此异常
    
    Examples:
        >>> function_name(1, 2)
        3
    """
    pass

1.11 常见错误与调试

1.11.1 常见错误

# 错误1:忘记写冒号
# def greet(name)  # 缺少冒号
#     print(f"Hello, {name}")

# 错误2:参数名拼写错误
def greet(name):
    print(f"Hello, {name}")

# greet(neme)  # NameError: name 'neme' is not defined

# 错误3:参数数量不匹配
def add(a, b):
    return a + b

# add(1)    # TypeError: add() missing 1 required argument
# add(1, 2, 3)  # TypeError: add() takes 2 positional arguments but 3 were given

# 错误4:修改不可变对象
# def modify_immutable(s):
#     s += " world"  # 创建了新字符串,原字符串不变

1.11.2 调试技巧

# 使用print调试
def buggy_function(n):
    print(f"[DEBUG] 输入: {n}")
    result = 0
    for i in range(n):
        result += i
        print(f"[DEBUG] i={i}, result={result}")
    return result

# 使用assert调试
def divide(a, b):
    assert b != 0, "除数不能为零"
    return a / b

二、背诵版

Day10 要点速记: 【函数定义】 def 函数名(参数1, 参数2, ...): """文档字符串""" 函数体 return 返回值 【参数类型】 - 位置参数:按顺序传递 - 关键字参数:按名称传递 - 默认参数:parameter=默认值 - *args:**kwargs 【返回值】 - 无return:返回None - return 值:返回单个值 - return 值1, 值2:返回元组 【文档字符串】 单行:"""简洁描述""" 多行:详细说明Args/Returns/Examples 【作用域】 LEGB: Local → Enclosing → Global → Built-in global关键字:在函数内修改全局变量

三、考前记忆

要素 内容
今日主题 函数定义与调用
关键词 函数定义、调用、参数、返回值、文档字符串
定义语法 def 函数名(参数):
返回值 无return返回None
多返回值 return a, b(返回元组)
文档字符串 函数后的三引号字符串
参数传递 位置参数、关键字参数、默认参数
重要考点 参数传递、返回值处理、作用域

四、测试题

1. 单选题: 以下哪个是函数定义的正确语法?

  • A. function greet(name):
  • B. def greet(name):
  • C. func greet(name):
  • D. greet(name) def:

2. 单选题: 函数没有return语句时,返回值是什么?

  • A. 0
  • B. ""
  • C. None
  • D. False

3. 单选题: 以下代码的输出是什么?

def test(a, b=10):
    return a + b

print(test(5))
  • A. 5
  • B. 10
  • C. 15
  • D. 错误

4. 填空题: return语句可以返回 _______(填"一个"或"多个")值。

5. 填空题: 在函数内部修改全局变量,需要使用 _______ 关键字。

6. 简答题: 请说明位置参数和关键字参数的区别。

7. 代码题: 定义一个函数,接受两个参数x和y,返回它们的和、差、积。

8. 代码题: 定义一个函数,计算一个列表中所有正整数的和。

9. 代码题: 为以下函数编写文档字符串:

def find_max(numbers):
    # 找出列表中的最大值
    if not numbers:
        return None
    max_val = numbers[0]
    for num in numbers:
        if num > max_val:
            max_val = num
    return max_val

10. 综合题: 设计一个计算器程序,包含加、减、乘、除四个函数,用户输入两个数和一个运算符,程序返回计算结果。

  1. B - Python函数定义使用def关键字

  2. C - 函数没有return语句时,默认返回None

  3. C - b使用默认值10,所以5+10=15

  4. 多个 - return可以返回多个值,实际是返回元组

  5. global - 使用global关键字可以在函数内修改全局变量

位置参数和关键字参数的区别: 位置参数: - 按照参数定义的顺序传递 - 必须传递所有必需的位置参数 - 例如:func(1, 2, 3) 关键字参数: - 按照参数名称传递 - 可以改变顺序 - 例如:func(a=1, c=3, b=2) 可以混合使用,但位置参数必须在关键字参数前面
def calculate(a, b):
    """返回a和b的和、差、积"""
    return a + b, a - b, a * b

sum_val, diff, prod = calculate(10, 3)
print(f"和: {sum_val}, 差: {diff}, 积: {prod}")
def sum_positive(numbers):
    """计算列表中所有正整数的和"""
    total = 0
    for num in numbers:
        if num > 0:
            total += num
    return total

print(sum_positive([1, -2, 3, -4, 5]))  # 9
def find_max(numbers):
    """
    找出列表中的最大值。
    
    Parameters:
        numbers (list): 数字列表
    
    Returns:
        number or None: 列表中的最大值,如果列表为空则返回None
    
    Examples:
        >>> find_max([1, 5, 3, 9, 2])
        9
        >>> find_max([])
        None
    """
    if not numbers:
        return None
    max_val = numbers[0]
    for num in numbers:
        if num > max_val:
            max_val = num
    return max_val
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 "错误:除数不能为零"
    return a / b

operations = {
    '+': add,
    '-': subtract,
    '*': multiply,
    '/': divide
}

def calculator():
    """简单计算器"""
    print("=== 简单计算器 ===")
    try:
        num1 = float(input("请输入第一个数: "))
        operator = input("请输入运算符(+,-,*,/): ")
        num2 = float(input("请输入第二个数: "))
        
        if operator not in operations:
            print("无效的运算符")
            return
        
        result = operations[operator](num1, num2)
        print(f"结果: {num1} {operator} {num2} = {result}")
    except ValueError:
        print("输入无效")

calculator()

五、扩展阅读

5.1 函数注解(Type Hints)

Python 3.5+支持函数注解,用于指定参数和返回值的类型:

def greet(name: str, times: int = 1) -> str:
    """打招呼"""
    return (f"Hello, {name}! " * times).strip()

# 使用函数注解
print(greet("Alice", 2))

# 运行时获取注解
print(greet.__annotations__)

5.2 高阶函数

函数可以像变量一样作为参数传递:

def apply_twice(func, value):
    """对值应用函数两次"""
    return func(func(value))

def square(x):
    return x ** 2

print(apply_twice(square, 3))  # (3²)² = 81

5.3 匿名函数(Lambda)

# 使用lambda表达式创建简短函数
square = lambda x: x ** 2
print(square(5))  # 25

# 与内置函数结合使用
numbers = [1, 2, 3, 4, 5]
squares = list(map(lambda x: x ** 2, numbers))
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(f"平方: {squares}")
print(f"偶数: {evens}")

恭喜完成Day10学习!Python基础循环与函数部分已全部完成。明天的Day11将进一步学习函数的返回值高级应用。