第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
2的3次方: 8
10的4次方: 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函数的各个方面:
- 函数定义与调用:学习了如何定义函数、编写文档字符串以及调用函数
- 参数传递:掌握了位置参数、关键字参数、默认参数的使用方法
- 可变参数:了解了args和*kwargs的用法,以及参数解包操作
- 返回值与作用域:学习了函数返回值的处理和变量作用域的概念
- 递归函数:掌握了递归的基本原理和经典应用
- lambda表达式:学会了使用lambda创建匿名函数
- 内置函数:详细了解了Python提供的各种内置函数
函数是Python编程的核心概念,掌握好函数的使用对于编写高质量的Python代码至关重要。在实际编程中,要根据具体需求选择合适的函数定义方式和参数传递方法,同时注意作用域的管理和递归的合理使用。