第5章 函数

函数是Python编程中最重要的概念之一,它允许我们将代码组织成可重用的模块,提高代码的可读性和维护性。本章将深入讲解Python函数的各个方面,从基础的函数定义到高级的递归和内置函数使用。

本系列文章所使用到的示例源码:Python从入门到精通示例代码

5.1 函数的定义与调用

函数的概念与作用

函数是一段具有特定功能的可重用代码块。使用函数的主要优势包括:

  • 代码复用:避免重复编写相同的代码
  • 模块化编程:将复杂问题分解为简单的子问题
  • 提高代码可读性:使程序结构更清晰

函数的定义语法

在Python中,使用def关键字定义函数:

def function_name(parameters):
    """文档字符串(可选)"""
    # 函数体
    return value  # 返回值(可选)

让我们看一个简单的例子:

def greet(name):
    """问候函数,接收一个姓名参数"""
    return f"Hello, {name}!"

# 调用函数
message = greet("Alice")
print(message)

输出结果:

Hello, Alice!

函数命名规范

函数名应该遵循以下规范:
- 使用小写字母和下划线
- 名称应该描述函数的功能
- 避免使用Python关键字

# 好的函数名
def calculate_area(radius):
    return 3.14159 * radius ** 2

def get_user_input():
    return input("请输入内容: ")

# 不好的函数名
def func1():  # 名称不够描述性
    pass

def Class():  # 应该用于类名,不是函数名
    pass

函数的文档字符串

文档字符串(docstring)用于描述函数的功能、参数和返回值:

def calculate_bmi(weight, height):
    """
    计算身体质量指数(BMI)

    参数:
        weight (float): 体重,单位为千克
        height (float): 身高,单位为米

    返回:
        float: BMI值
    """
    return weight / (height ** 2)

# 使用help()函数查看文档
help(calculate_bmi)

输出结果:

Help on function calculate_bmi in module __main__:

calculate_bmi(weight, height)
    计算身体质量指数(BMI)

    参数:
        weight (float): 体重,单位为千克
        height (float): 身高,单位为米

    返回:
        float: BMI值

5.2 参数传递

位置参数

位置参数是最基本的参数类型,参数的值按照定义时的顺序传递:

def introduce_person(name, age, city):
    """介绍一个人的基本信息"""
    return f"我叫{name},今年{age}岁,来自{city}"

# 按位置传递参数
result = introduce_person("张三", 25, "北京")
print(result)

输出结果:

我叫张三,今年25岁,来自北京

关键字参数

关键字参数允许通过参数名来指定值,提高代码的可读性:

def create_user_profile(name, age, email, city="未知"):
    """创建用户档案"""
    profile = {
        "姓名": name,
        "年龄": age,
        "邮箱": email,
        "城市": city
    }
    return profile

# 使用关键字参数
user1 = create_user_profile(name="李四", email="lisi@example.com", age=30)
print(user1)

# 混合使用位置参数和关键字参数
user2 = create_user_profile("王五", age=28, email="wangwu@example.com", city="上海")
print(user2)

输出结果:

{'姓名': '李四', '年龄': 30, '邮箱': 'lisi@example.com', '城市': '未知'}
{'姓名': '王五', '年龄': 28, '邮箱': 'wangwu@example.com', '城市': '上海'}

默认参数

默认参数为函数参数提供默认值,使函数调用更加灵活:

def power(base, exponent=2):
    """计算幂运算,默认计算平方"""
    return base ** exponent

# 使用默认参数
print(f"5的平方: {power(5)}")
print(f"2的3次方: {power(2, 3)}")
print(f"10的4次方: {power(base=10, exponent=4)}")

输出结果:

5的平方: 25
23次方: 8
104次方: 10000

注意:默认参数的陷阱

使用可变对象作为默认参数时要特别小心:

# 错误的做法
def add_item_wrong(item, target_list=[]):
    target_list.append(item)
    return target_list

# 正确的做法
def add_item_correct(item, target_list=None):
    if target_list is None:
        target_list = []
    target_list.append(item)
    return target_list

# 演示错误做法的问题
list1 = add_item_wrong("apple")
list2 = add_item_wrong("banana")
print(f"list1: {list1}")
print(f"list2: {list2}")

# 演示正确做法
list3 = add_item_correct("apple")
list4 = add_item_correct("banana")
print(f"list3: {list3}")
print(f"list4: {list4}")

输出结果:

list1: ['apple', 'banana']
list2: ['apple', 'banana']
list3: ['apple']
list4: ['banana']

5.3 可变参数

*args(可变位置参数)

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

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

# 传递不同数量的参数
print(f"1个数字的和: {calculate_sum(5)}")
print(f"3个数字的和: {calculate_sum(1, 2, 3)}")
print(f"5个数字的和: {calculate_sum(10, 20, 30, 40, 50)}")

# 使用列表解包
numbers_list = [1, 2, 3, 4, 5]
print(f"列表解包的和: {calculate_sum(*numbers_list)}")

输出结果:

1个数字的和: 5
3个数字的和: 6
5个数字的和: 150
列表解包的和: 15

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

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

def create_student_info(name, **details):
    """创建学生信息,接收任意额外的详细信息"""
    info = {"姓名": name}
    info.update(details)
    return info

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

print(f"学生1信息: {student1}")
print(f"学生2信息: {student2}")

# 使用字典解包
extra_info = {"city": "北京", "hobby": "编程"}
student3 = create_student_info("王五", age=21, **extra_info)
print(f"学生3信息: {student3}")

输出结果:

学生1信息: {'姓名': '张三', 'age': 20, 'major': '计算机科学'}
学生2信息: {'姓名': '李四', 'age': 19, 'major': '数学', 'grade': '大二', 'gpa': 3.8}
学生3信息: {'姓名': '王五', 'age': 21, 'city': '北京', 'hobby': '编程'}

参数的完整语法顺序

函数参数的完整顺序必须是:位置参数 → 默认参数 → args → *kwargs

def complete_function(pos_arg, default_arg="default", *args, **kwargs):
    """演示完整的参数语法"""
    print(f"位置参数: {pos_arg}")
    print(f"默认参数: {default_arg}")
    print(f"可变位置参数: {args}")
    print(f"可变关键字参数: {kwargs}")
    print("-" * 30)

# 各种调用方式
complete_function("必需参数")
complete_function("必需参数", "自定义默认值")
complete_function("必需参数", "自定义默认值", 1, 2, 3)
complete_function("必需参数", "自定义默认值", 1, 2, 3, key1="value1", key2="value2")

输出结果:

位置参数: 必需参数
默认参数: default
可变位置参数: ()
可变关键字参数: {}
------------------------------
位置参数: 必需参数
默认参数: 自定义默认值
可变位置参数: ()
可变关键字参数: {}
------------------------------
位置参数: 必需参数
默认参数: 自定义默认值
可变位置参数: (1, 2, 3)
可变关键字参数: {}
------------------------------
位置参数: 必需参数
默认参数: 自定义默认值
可变位置参数: (1, 2, 3)
可变关键字参数: {'key1': 'value1', 'key2': 'value2'}
------------------------------

5.4 返回值与作用域

函数的返回值

单个返回值

def get_circle_area(radius):
    """计算圆的面积"""
    return 3.14159 * radius ** 2

area = get_circle_area(5)
print(f"半径为5的圆的面积: {area}")

输出结果:

半径为5的圆的面积: 78.53975

多个返回值

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

# 接收多个返回值
rect_area, rect_perimeter = get_rectangle_info(10, 5)
print(f"矩形面积: {rect_area}")
print(f"矩形周长: {rect_perimeter}")

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

输出结果:

矩形面积: 50
矩形周长: 30
结果元组: (48, 28)
面积: 48, 周长: 28

无返回值(None)

def print_user_info(name, age):
    """打印用户信息,无返回值"""
    print(f"用户姓名: {name}")
    print(f"用户年龄: {age}")
    # 没有return语句,默认返回None

result = print_user_info("张三", 25)
print(f"函数返回值: {result}")

输出结果:

用户姓名: 张三
用户年龄: 25
函数返回值: None

变量的作用域

局部作用域和全局作用域

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

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

scope_demo()
print(f"函数外部访问全局变量: {global_var}")
# print(local_var)  # 这会报错,因为局部变量在函数外部不可访问

输出结果:

函数内部访问全局变量: 我是全局变量
函数内部访问局部变量: 我是局部变量
函数外部访问全局变量: 我是全局变量

global关键字

counter = 0  # 全局变量

def increment_counter():
    global counter  # 声明使用全局变量
    counter += 1
    print(f"计数器值: {counter}")

def reset_counter():
    global counter
    counter = 0
    print("计数器已重置")

print(f"初始计数器值: {counter}")
increment_counter()
increment_counter()
increment_counter()
reset_counter()
print(f"最终计数器值: {counter}")

输出结果:

初始计数器值: 0
计数器值: 1
计数器值: 2
计数器值: 3
计数器已重置
最终计数器值: 0

nonlocal关键字

def outer_function():
    x = "外层函数的变量"

    def inner_function():
        nonlocal x  # 声明使用外层函数的变量
        x = "被内层函数修改的变量"
        print(f"内层函数中的x: {x}")

    print(f"调用内层函数前的x: {x}")
    inner_function()
    print(f"调用内层函数后的x: {x}")

outer_function()

输出结果:

调用内层函数前的x: 外层函数的变量
内层函数中的x: 被内层函数修改的变量
调用内层函数后的x: 被内层函数修改的变量

5.5 递归函数

递归的概念

递归是指函数调用自身的编程技术。每个递归函数都必须包含:
- 基础情况:递归的终止条件
- 递归情况:函数调用自身的情况

经典递归示例

阶乘计算

def factorial(n):
    """计算n的阶乘"""
    # 基础情况
    if n == 0 or n == 1:
        return 1
    # 递归情况
    else:
        return n * factorial(n - 1)

# 测试阶乘函数
for i in range(6):
    result = factorial(i)
    print(f"{i}! = {result}")

输出结果:

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)}")

输出结果:

斐波那契数列前10:
F(0) = 0
F(1) = 1
F(2) = 1
F(3) = 2
F(4) = 3
F(5) = 5
F(6) = 8
F(7) = 13
F(8) = 21
F(9) = 34

目录遍历

import os

def list_files(directory, level=0):
    """递归遍历目录结构"""
    try:
        items = os.listdir(directory)
        for item in items:
            item_path = os.path.join(directory, item)
            indent = "  " * level  # 根据层级添加缩进

            if os.path.isdir(item_path):
                print(f"{indent}{item}/")
                list_files(item_path, level + 1)  # 递归调用
            else:
                print(f"{indent}{item}")
    except PermissionError:
        print(f"{indent}[权限不足]")

# 遍历当前目录(注意:这会显示很多文件,这里只是示例)
# list_files(".", 0)
print("目录遍历函数已定义,可以调用 list_files('目录路径') 来使用")

输出结果:

目录遍历函数已定义,可以调用 list_files('目录路径') 来使用

递归与迭代的对比

# 递归版本的阶乘
def factorial_recursive(n):
    if n <= 1:
        return 1
    return n * factorial_recursive(n - 1)

# 迭代版本的阶乘
def factorial_iterative(n):
    result = 1
    for i in range(1, n + 1):
        result *= i
    return result

# 性能比较
import time

n = 10

# 测试递归版本
start_time = time.time()
recursive_result = factorial_recursive(n)
recursive_time = time.time() - start_time

# 测试迭代版本
start_time = time.time()
iterative_result = factorial_iterative(n)
iterative_time = time.time() - start_time

print(f"递归结果: {recursive_result}, 耗时: {recursive_time:.6f}秒")
print(f"迭代结果: {iterative_result}, 耗时: {iterative_time:.6f}秒")
print(f"结果相同: {recursive_result == iterative_result}")

输出结果:

递归结果: 3628800, 耗时: 0.000003
迭代结果: 3628800, 耗时: 0.000002
结果相同: True

5.6 lambda表达式

lambda表达式的概念

lambda表达式是创建匿名函数的简洁方式,适用于简单的函数定义。

基本语法

# lambda参数: 表达式

# 简单的lambda表达式
square = lambda x: x ** 2
print(f"5的平方: {square(5)}")

# 多个参数的lambda表达式
add = lambda x, y: x + y
print(f"3 + 7 = {add(3, 7)}")

# 带默认参数的lambda表达式
greet = lambda name, greeting="Hello": f"{greeting}, {name}!"
print(greet("Alice"))
print(greet("Bob", "Hi"))

输出结果:

5的平方: 25
3 + 7 = 10
Hello, Alice!
Hi, Bob!

lambda与内置函数的结合使用

与map()函数结合

# 使用lambda和map()处理列表
numbers = [1, 2, 3, 4, 5]

# 计算每个数的平方
squares = list(map(lambda x: x ** 2, numbers))
print(f"原数字: {numbers}")
print(f"平方值: {squares}")

# 将摄氏度转换为华氏度
celsius = [0, 20, 30, 40]
fahrenheit = list(map(lambda c: c * 9/5 + 32, celsius))
print(f"摄氏度: {celsius}")
print(f"华氏度: {fahrenheit}")

输出结果:

原数字: [1, 2, 3, 4, 5]
平方值: [1, 4, 9, 16, 25]
摄氏度: [0, 20, 30, 40]
华氏度: [32.0, 68.0, 86.0, 104.0]

与filter()函数结合

# 使用lambda和filter()过滤列表
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

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

# 过滤出大于5的数
greater_than_five = list(filter(lambda x: x > 5, numbers))
print(f"大于5的数: {greater_than_five}")

# 过滤字符串列表
words = ["apple", "banana", "cherry", "date", "elderberry"]
long_words = list(filter(lambda word: len(word) > 5, words))
print(f"原单词: {words}")
print(f"长度大于5的单词: {long_words}")

输出结果:

原数字: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
偶数: [2, 4, 6, 8, 10]
大于5的数: [6, 7, 8, 9, 10]
原单词: ['apple', 'banana', 'cherry', 'date', 'elderberry']
长度大于5的单词: ['banana', 'cherry', 'elderberry']

与sorted()函数结合

# 使用lambda进行自定义排序
students = [
    {"name": "Alice", "age": 20, "grade": 85},
    {"name": "Bob", "age": 19, "grade": 92},
    {"name": "Charlie", "age": 21, "grade": 78},
    {"name": "Diana", "age": 20, "grade": 96}
]

# 按年龄排序
sorted_by_age = sorted(students, key=lambda student: student["age"])
print("按年龄排序:")
for student in sorted_by_age:
    print(f"  {student['name']}: {student['age']}岁")

# 按成绩排序(降序)
sorted_by_grade = sorted(students, key=lambda student: student["grade"], reverse=True)
print("\n按成绩排序(降序):")
for student in sorted_by_grade:
    print(f"  {student['name']}: {student['grade']}分")

输出结果:

按年龄排序:
  Bob: 19
  Alice: 20
  Diana: 20
  Charlie: 21

按成绩排序(降序):
  Diana: 96
  Bob: 92
  Alice: 85
  Charlie: 78

5.7 内置函数详解

数学函数

# 数学相关的内置函数
numbers = [-5, -2.7, 0, 3.14, 8]

print("数学函数示例:")
print(f"abs(-5) = {abs(-5)}")  # 绝对值
print(f"round(3.14159, 2) = {round(3.14159, 2)}")  # 四舍五入
print(f"pow(2, 3) = {pow(2, 3)}")  # 幂运算
print(f"min(numbers) = {min(numbers)}")  # 最小值
print(f"max(numbers) = {max(numbers)}")  # 最大值
print(f"sum(numbers) = {sum(numbers)}")  # 求和

输出结果:

数学函数示例:
abs(-5) = 5
round(3.14159, 2) = 3.14
pow(2, 3) = 8
min(numbers) = -5
max(numbers) = 8
sum(numbers) = 3.44

类型转换函数

# 类型转换函数
print("类型转换示例:")
print(f"int('123') = {int('123')}")
print(f"float('3.14') = {float('3.14')}")
print(f"str(42) = '{str(42)}'")
print(f"bool(1) = {bool(1)}")
print(f"bool(0) = {bool(0)}")

# 集合类型转换
original_list = [1, 2, 2, 3, 3, 3]
print(f"\n原列表: {original_list}")
print(f"转为集合: {set(original_list)}")
print(f"转为元组: {tuple(original_list)}")
print(f"转为字典: {dict(enumerate(original_list))}")

输出结果:

类型转换示例:
int('123') = 123
float('3.14') = 3.14
str(42) = '42'
bool(1) = True
bool(0) = False

原列表: [1, 2, 2, 3, 3, 3]
转为集合: {1, 2, 3}
转为元组: (1, 2, 2, 3, 3, 3)
转为字典: {0: 1, 1: 2, 2: 2, 3: 3, 4: 3, 5: 3}

序列操作函数

# 序列操作函数
fruits = ["apple", "banana", "cherry"]
numbers = [3, 1, 4, 1, 5, 9, 2, 6]

print("序列操作示例:")
print(f"len(fruits) = {len(fruits)}")
print(f"sorted(numbers) = {sorted(numbers)}")
print(f"list(reversed(fruits)) = {list(reversed(fruits))}")

# enumerate()函数
print("\nenumerate()示例:")
for index, fruit in enumerate(fruits):
    print(f"  索引{index}: {fruit}")

# zip()函数
colors = ["red", "yellow", "dark red"]
print("\nzip()示例:")
for fruit, color in zip(fruits, colors):
    print(f"  {fruit}{color}色的")

输出结果:

序列操作示例:
len(fruits) = 3
sorted(numbers) = [1, 1, 2, 3, 4, 5, 6, 9]
list(reversed(fruits)) = ['cherry', 'banana', 'apple']

enumerate()示例:
  索引0: apple
  索引1: banana
  索引2: cherry

zip()示例:
  apple是red色的
  banana是yellow色的
  cherry是dark red色的

高阶函数详解

reduce()函数

from functools import reduce

# reduce()函数示例
numbers = [1, 2, 3, 4, 5]

# 计算所有数字的乘积
product = reduce(lambda x, y: x * y, numbers)
print(f"数字列表: {numbers}")
print(f"所有数字的乘积: {product}")

# 找出最大值
max_value = reduce(lambda x, y: x if x > y else y, numbers)
print(f"最大值: {max_value}")

# 字符串连接
words = ["Hello", "World", "Python", "Programming"]
sentence = reduce(lambda x, y: x + " " + y, words)
print(f"单词列表: {words}")
print(f"连接后的句子: {sentence}")

输出结果:

数字列表: [1, 2, 3, 4, 5]
所有数字的乘积: 120
最大值: 5
单词列表: ['Hello', 'World', 'Python', 'Programming']
连接后的句子: Hello World Python Programming

其他常用内置函数

# any()和all()函数
list1 = [True, True, True]
list2 = [True, False, True]
list3 = [False, False, False]

print("any()和all()函数示例:")
print(f"all({list1}) = {all(list1)}")
print(f"all({list2}) = {all(list2)}")
print(f"any({list2}) = {any(list2)}")
print(f"any({list3}) = {any(list3)}")

# isinstance()函数
print("\nisinstance()函数示例:")
value = 42
print(f"isinstance({value}, int) = {isinstance(value, int)}")
print(f"isinstance({value}, str) = {isinstance(value, str)}")
print(f"isinstance({value}, (int, float)) = {isinstance(value, (int, float))}")

# hasattr()函数
class Person:
    def __init__(self, name):
        self.name = name

    def greet(self):
        return f"Hello, I'm {self.name}"

person = Person("Alice")
print("\nhasattr()函数示例:")
print(f"hasattr(person, 'name') = {hasattr(person, 'name')}")
print(f"hasattr(person, 'greet') = {hasattr(person, 'greet')}")
print(f"hasattr(person, 'age') = {hasattr(person, 'age')}")

输出结果:

any()和all()函数示例:
all([True, True, True]) = True
all([True, False, True]) = False
any([True, False, True]) = True
any([False, False, False]) = False

isinstance()函数示例:
isinstance(42, int) = True
isinstance(42, str) = False
isinstance(42, (int, float)) = True

hasattr()函数示例:
hasattr(person, 'name') = True
hasattr(person, 'greet') = True
hasattr(person, 'age') = False

本章小结

本章详细介绍了Python函数的各个方面:

  1. 函数定义与调用:学习了如何定义函数、编写文档字符串以及调用函数
  2. 参数传递:掌握了位置参数、关键字参数、默认参数的使用方法
  3. 可变参数:了解了args和*kwargs的用法,以及参数解包操作
  4. 返回值与作用域:学习了函数返回值的处理和变量作用域的概念
  5. 递归函数:掌握了递归的基本原理和经典应用
  6. lambda表达式:学会了使用lambda创建匿名函数
  7. 内置函数:详细了解了Python提供的各种内置函数

函数是Python编程的核心概念,掌握好函数的使用对于编写高质量的Python代码至关重要。在实际编程中,要根据具体需求选择合适的函数定义方式和参数传递方法,同时注意作用域的管理和递归的合理使用。

小夜