第5章:Python 函数
5.1 函数的定义和调用
函数是一段可重用的代码块,用于执行特定的任务。
函数的定义
使用 def 关键字来定义函数:
def greet():
"""打印问候语"""
print("Hello, World!")
# 函数调用
greet()
函数文档字符串
函数可以包含文档字符串(docstring),用于描述函数的功能:
def greet(name):
"""打印问候语
Args:
name (str): 用户名
Returns:
None
"""
print(f"Hello, {name}!")
# 查看文档字符串
print(greet.__doc__)
5.2 参数传递
函数可以接受参数,参数是函数执行所需的输入。
位置参数
位置参数是最常见的参数类型,按照位置顺序传递:
def add(a, b):
"""计算两个数的和"""
return a + b
result = add(3, 5)
print(result) # 8
关键字参数
关键字参数使用参数名来传递,可以不按照位置顺序:
def greet(name, age):
"""打印问候语和年龄"""
print(f"Hello, {name}! You are {age} years old.")
# 使用关键字参数
greet(age=30, name="John")
# 混合使用位置参数和关键字参数
greet("John", age=30)
默认参数
默认参数在定义函数时指定默认值:
def greet(name, greeting="Hello"):
"""打印问候语"""
print(f"{greeting}, {name}!")
# 使用默认值
greet("John") # Hello, John!
# 覆盖默认值
greet("John", "Hi") # Hi, John!
可变参数
可变参数允许函数接受任意数量的参数:
# *args:接收任意数量的位置参数
def add(*args):
"""计算任意数量数的和"""
total = 0
for num in args:
total += num
return total
print(add(1, 2, 3, 4, 5)) # 15
# **kwargs:接收任意数量的关键字参数
def print_info(**kwargs):
"""打印任意数量的关键字参数"""
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(name="John", age=30, city="New York")
# 混合使用
def mixed(a, b, *args, c=10, **kwargs):
print(f"a: {a}, b: {b}, c: {c}")
print(f"args: {args}")
print(f"kwargs: {kwargs}")
mixed(1, 2, 3, 4, 5, c=20, d=30, e=40)
5.3 返回值
函数可以使用 return 语句返回值。
基本返回值
def add(a, b):
"""计算两个数的和"""
return a + b
result = add(3, 5)
print(result) # 8
返回多个值
def get_name_and_age():
"""返回姓名和年龄"""
name = "John"
age = 30
return name, age # 返回元组
name, age = get_name_and_age()
print(name, age) # John 30
# 也可以这样接收
result = get_name_and_age()
print(result) # ('John', 30)
print(result[0]) # John
print(result[1]) # 30
无返回值
如果函数没有 return 语句,或者 return 语句没有值,函数会返回 None:
def greet():
"""打印问候语"""
print("Hello!")
result = greet()
print(result) # None
# 显式返回 None
def do_nothing():
"""什么都不做"""
return None
result = do_nothing()
print(result) # None
5.4 局部变量和全局变量
局部变量
局部变量是在函数内部定义的变量,只在函数内部有效:
def my_function():
"""测试局部变量"""
x = 10 # 局部变量
print(f"Inside function: x = {x}")
my_function()
try:
print(f"Outside function: x = {x}") # 会报错,因为 x 是局部变量
except NameError as e:
print(f"Error: {e}")
全局变量
全局变量是在函数外部定义的变量,可以在函数内部访问:
x = 10 # 全局变量
def my_function():
"""测试全局变量"""
print(f"Inside function: x = {x}")
my_function()
print(f"Outside function: x = {x}")
在函数内部修改全局变量
如果要在函数内部修改全局变量,需要使用 global 关键字:
x = 10 # 全局变量
def my_function():
"""修改全局变量"""
global x
x = 20
print(f"Inside function: x = {x}")
my_function()
print(f"Outside function: x = {x}") # 20
非局部变量
在嵌套函数中,使用 nonlocal 关键字来访问和修改外部函数的变量:
def outer_function():
"""外部函数"""
x = 10
def inner_function():
"""内部函数"""
nonlocal x
x = 20
print(f"Inside inner function: x = {x}")
inner_function()
print(f"Inside outer function: x = {x}")
outer_function()
print(f"Outside function: x = {x}") # 会报错,因为 x 是外部函数的局部变量
5.5 递归函数
递归函数是调用自身的函数。
基本递归
def factorial(n):
"""计算阶乘"""
if n == 0:
return 1
else:
return n * factorial(n - 1)
print(factorial(5)) # 120
递归的注意事项
- 基线条件:递归函数必须有一个基线条件(终止条件),否则会导致无限递归
- 递归深度:Python 有递归深度限制,默认是 1000
- 性能:递归可能比迭代慢,因为每次函数调用都会创建新的栈帧
其他递归示例
# 斐波那契数列
def fibonacci(n):
"""计算斐波那契数列的第 n 项"""
if n <= 1:
return n
else:
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10)) # 55
# 二分查找
def binary_search(arr, low, high, target):
"""二分查找"""
if high >= low:
mid = (low + high) // 2
if arr[mid] == target:
return mid
elif arr[mid] > target:
return binary_search(arr, low, mid-1, target)
else:
return binary_search(arr, mid+1, high, target)
else:
return -1
arr = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
target = 7
result = binary_search(arr, 0, len(arr)-1, target)
print(f"Element found at index: {result}")
5.6 匿名函数(lambda)
匿名函数是使用 lambda 关键字创建的小型、匿名的函数。
基本语法
# 基本语法
lambda arguments: expression
# 示例
double = lambda x: x * 2
print(double(5)) # 10
# 多个参数
sum = lambda a, b: a + b
print(sum(3, 5)) # 8
# 无参数
greet = lambda: "Hello, World!"
print(greet()) # Hello, World!
与内置函数一起使用
# 与 map() 一起使用
numbers = [1, 2, 3, 4, 5]
doubled = list(map(lambda x: x * 2, numbers))
print(doubled) # [2, 4, 6, 8, 10]
# 与 filter() 一起使用
numbers = [1, 2, 3, 4, 5]
even = list(filter(lambda x: x % 2 == 0, numbers))
print(even) # [2, 4]
# 与 sorted() 一起使用
students = [
{"name": "John", "grade": 85},
{"name": "Alice", "grade": 92},
{"name": "Bob", "grade": 78}
]
sorted_students = sorted(students, key=lambda student: student["grade"], reverse=True)
print(sorted_students)
作为参数传递
def apply_function(func, x):
"""应用函数到值"""
return func(x)
result = apply_function(lambda x: x ** 2, 5)
print(result) # 25
# 作为返回值
def make_multiplier(n):
"""返回一个乘法函数"""
return lambda x: x * n
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
5.7 函数的高级特性
装饰器
装饰器是修改其他函数行为的函数:
def log_function(func):
"""日志装饰器"""
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned {result}")
return result
return wrapper
@log_function
def add(a, b):
"""计算两个数的和"""
return a + b
result = add(3, 5)
print(result) # 8
装饰器的高级用法
带参数的装饰器:
def repeat(n):
"""重复执行函数n次的装饰器"""
def decorator(func):
def wrapper(*args, **kwargs):
results = []
for i in range(n):
print(f"Execution {i+1}")
results.append(func(*args, **kwargs))
return results
return wrapper
return decorator
@repeat(3)
def greet(name):
return f"Hello, {name}!"
print(greet("Alice"))
# Execution 1
# Execution 2
# Execution 3
# ['Hello, Alice!', 'Hello, Alice!', 'Hello, Alice!']
多个装饰器:
def uppercase(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result.upper()
return wrapper
def exclaim(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result + "!"
return wrapper
@uppercase
@exclaim
def greet(name):
return f"Hello, {name}"
print(greet("Bob")) # HELLO, BOB!
保留函数元数据:
import functools
def log_function(func):
"""日志装饰器"""
@functools.wraps(func) # 保留原函数的元数据
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned {result}")
return result
return wrapper
@log_function
def add(a, b):
"""计算两个数的和"""
return a + b
print(add.__name__) # add
print(add.__doc__) # 计算两个数的和
生成器
生成器是一种特殊的函数,使用 yield 语句返回值,可以节省内存:
def countdown(n):
"""倒计时生成器"""
while n > 0:
yield n
n -= 1
# 使用生成器
for num in countdown(5):
print(num) # 5 4 3 2 1
# 生成器表达式(类似于列表推导式)
squares = (x**2 for x in range(5))
for square in squares:
print(square) # 0 1 4 9 16
迭代器
迭代器是实现了 __iter__() 和 __next__() 方法的对象:
class MyIterator:
def __init__(self, start, end):
self.current = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.current >= self.end:
raise StopIteration
value = self.current
self.current += 1
return value
# 使用迭代器
my_iter = MyIterator(1, 5)
for num in my_iter:
print(num) # 1 2 3 4
# 内置迭代器函数
numbers = [1, 2, 3, 4, 5]
iter_obj = iter(numbers)
print(next(iter_obj)) # 1
print(next(iter_obj)) # 2
上下文管理器
上下文管理器使用 with 语句管理资源,确保资源正确释放:
# 使用内置上下文管理器
with open('example.txt', 'w') as file:
file.write('Hello, World!')
# 文件自动关闭
# 自定义上下文管理器
class Timer:
def __enter__(self):
import time
self.start = time.time()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
import time
self.end = time.time()
print(f"Elapsed time: {self.end - self.start:.2f} seconds")
with Timer():
# 执行一些操作
import time
time.sleep(1)
# 输出: Elapsed time: 1.00 seconds
# 使用 contextmanager 装饰器
from contextlib import contextmanager
@contextmanager
def temporary_file():
# 准备资源
import tempfile
temp = tempfile.NamedTemporaryFile(mode='w', delete=False)
try:
yield temp
finally:
# 清理资源
import os
os.unlink(temp.name)
with temporary_file() as f:
f.write('Temporary data')
print(f.name) # 临时文件路径
函数作为对象
在 Python 中,函数是一等公民,可以作为对象处理:
# 函数赋值给变量
def greet():
"""打印问候语"""
return "Hello!"
message = greet
print(message()) # Hello!
# 函数作为列表元素
functions = [greet, len, max]
for func in functions:
try:
print(func())
except TypeError:
print(f"{func.__name__} requires arguments")
# 函数作为字典值
function_dict = {
"greet": greet,
"length": len,
"maximum": max
}
print(function_dict["greet"]()) # Hello!
5.8 综合示例
示例1:计算器函数
def calculator(operation, a, b):
"""简单计算器"""
if operation == "add":
return a + b
elif operation == "subtract":
return a - b
elif operation == "multiply":
return a * b
elif operation == "divide":
if b == 0:
return "Error: Division by zero"
return a / b
else:
return "Error: Invalid operation"
print(calculator("add", 5, 3)) # 8
print(calculator("subtract", 5, 3)) # 2
print(calculator("multiply", 5, 3)) # 15
print(calculator("divide", 5, 3)) # 1.6666666666666667
print(calculator("divide", 5, 0)) # Error: Division by zero
print(calculator("power", 5, 3)) # Error: Invalid operation
示例2:素数检查器
def is_prime(n):
"""检查一个数是否是素数"""
if n <= 1:
return False
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
return False
return True
# 打印 1 到 100 之间的素数
print("Prime numbers between 1 and 100:")
for i in range(1, 101):
if is_prime(i):
print(i, end=" ")
print()
5.9 常见问题和解决方案
问题1:递归深度超出限制
# 错误示例
def infinite_recursion():
infinite_recursion()
# 解决方案:确保有基线条件
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n - 1)
问题2:变量作用域错误
# 错误示例
def my_function():
print(x) # 会报错,因为 x 还没有定义
x = 10
# 解决方案:先定义变量,再使用
def my_function():
x = 10
print(x)
问题3:默认参数是可变对象
# 错误示例
def add_to_list(item, my_list=[]):
my_list.append(item)
return my_list
print(add_to_list(1)) # [1]
print(add_to_list(2)) # [1, 2] # 意外行为
# 解决方案:使用 None 作为默认值
def add_to_list(item, my_list=None):
if my_list is None:
my_list = []
my_list.append(item)
return my_list
print(add_to_list(1)) # [1]
print(add_to_list(2)) # [2] # 正确行为
5.10 练习
- 函数定义练习:定义一个函数,计算两个数的最大值
- 参数练习:定义一个函数,接受任意数量的参数,返回它们的和
- 递归练习:定义一个递归函数,计算斐波那契数列的第 n 项
- lambda 练习:使用 lambda 函数和 map(),将列表中的所有元素转换为大写
- 综合练习:定义一个函数,检查一个字符串是否是回文
- 挑战练习:定义一个函数,生成指定长度的随机密码
5.11 小结
本章我们学习了:
- 函数的定义和调用
- 参数传递(位置参数、关键字参数、默认参数、可变参数)
- 返回值
- 局部变量和全局变量
- 递归函数
- 匿名函数(lambda)
- 函数的高级特性
现在你已经掌握了 Python 的函数,可以开始学习 Python 的数据结构了!