跳到主要内容

第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

递归的注意事项

  1. 基线条件:递归函数必须有一个基线条件(终止条件),否则会导致无限递归
  2. 递归深度:Python 有递归深度限制,默认是 1000
  3. 性能:递归可能比迭代慢,因为每次函数调用都会创建新的栈帧

其他递归示例

# 斐波那契数列
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 练习

  1. 函数定义练习:定义一个函数,计算两个数的最大值
  2. 参数练习:定义一个函数,接受任意数量的参数,返回它们的和
  3. 递归练习:定义一个递归函数,计算斐波那契数列的第 n 项
  4. lambda 练习:使用 lambda 函数和 map(),将列表中的所有元素转换为大写
  5. 综合练习:定义一个函数,检查一个字符串是否是回文
  6. 挑战练习:定义一个函数,生成指定长度的随机密码

5.11 小结

本章我们学习了:

  • 函数的定义和调用
  • 参数传递(位置参数、关键字参数、默认参数、可变参数)
  • 返回值
  • 局部变量和全局变量
  • 递归函数
  • 匿名函数(lambda)
  • 函数的高级特性

现在你已经掌握了 Python 的函数,可以开始学习 Python 的数据结构了!