====== 第三章 函数 ======
===== 本章概要 =====
函数是组织好的、可重复使用的代码块,用于实现单一或相关联的功能。本章将深入学习Python函数的定义、参数传递、返回值、lambda表达式和装饰器等高级特性。
===== 3.1 函数基础 =====
==== 3.1.1 定义函数 ====
使用 ``def`` 关键字定义函数。
# 基本函数定义
def greet():
"""这是一个问候函数"""
print("Hello, World!")
# 调用函数
greet() # Hello, World!
==== 3.1.2 函数参数 ====
# 带参数的函数
def greet_person(name):
"""向指定的人问候"""
print(f"Hello, {name}!")
greet_person("Alice") # Hello, Alice!
greet_person("Bob") # Hello, Bob!
# 多个参数
def add(a, b):
"""返回两个数的和"""
return a + b
result = add(3, 5)
print(result) # 8
==== 3.1.3 返回值 ====
# 返回单个值
def square(x):
return x ** 2
# 返回多个值(实际是返回元组)
def get_min_max(numbers):
return min(numbers), max(numbers)
minimum, maximum = get_min_max([3, 1, 4, 1, 5, 9])
print(f"最小值: {minimum}, 最大值: {maximum}")
# 没有return语句,默认返回None
def print_message(msg):
print(msg)
result = print_message("Hello")
print(result) # None
==== 3.1.4 文档字符串 ====
def calculate_area(length, width):
"""
计算矩形的面积。
参数:
length (float): 矩形的长度
width (float): 矩形的宽度
返回:
float: 矩形的面积
示例:
>>> calculate_area(5, 3)
15
"""
return length * width
# 查看文档字符串
print(calculate_area.__doc__)
help(calculate_area)
===== 3.2 参数类型 =====
==== 3.2.1 位置参数 ====
按照位置顺序传递参数。
def power(base, exponent):
return base ** exponent
print(power(2, 3)) # 8 (2的3次方)
print(power(3, 2)) # 9 (3的2次方)
==== 3.2.2 关键字参数 ====
通过参数名传递,可以任意顺序。
def introduce(name, age, city):
print(f"我叫{name},今年{age}岁,来自{city}")
# 关键字参数
introduce(age=25, city="北京", name="张三")
# 混合使用(位置参数必须在关键字参数之前)
introduce("李四", city="上海", age=30)
==== 3.2.3 默认参数值 ====
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
greet("Alice") # Hello, Alice!
greet("Bob", "Hi") # Hi, Bob!
greet("Charlie", greeting="Hey") # Hey, Charlie!
**⚠️ 警告:默认参数的陷阱**
# 错误示例
def append_item(item, item_list=[]):
item_list.append(item)
return item_list
print(append_item(1)) # [1]
print(append_item(2)) # [1, 2] - 意外!列表被保留了
# 正确做法
def append_item(item, item_list=None):
if item_list is None:
item_list = []
item_list.append(item)
return item_list
print(append_item(1)) # [1]
print(append_item(2)) # [2] - 正确
==== 3.2.4 可变参数 *args ====
接收任意数量的位置参数。
def sum_all(*args):
"""计算所有参数的和"""
total = 0
for num in args:
total += num
return total
# 调用方式
print(sum_all(1, 2, 3)) # 6
print(sum_all(1, 2, 3, 4, 5)) # 15
print(sum_all()) # 0
# args是一个元组
print(type(args)) #
==== 3.2.5 关键字可变参数 **kwargs ====
接收任意数量的关键字参数。
def print_info(**kwargs):
"""打印所有关键字参数"""
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(name="Alice", age=25, city="北京")
# 输出:
# name: Alice
# age: 25
# city: 北京
# kwargs是一个字典
print(type(kwargs)) #
==== 3.2.6 参数顺序规则 ====
正确的参数顺序:
def func(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2, **kwargs):
"""
参数顺序:
1. 仅限位置参数 (/ 之前)
2. 位置或关键字参数 (/ 和 * 之间)
3. 仅限关键字参数 (* 之后)
4. **kwargs
"""
pass
# 示例
def combined(a, b, /, c, d, *args, e, f, **kwargs):
print(f"a={a}, b={b}, c={c}, d={d}")
print(f"args={args}")
print(f"e={e}, f={f}")
print(f"kwargs={kwargs}")
combined(1, 2, 3, 4, 5, 6, e=7, f=8, g=9, h=10)
==== 3.2.7 解包参数 ====
# 使用*解包列表/元组
def add(a, b, c):
return a + b + c
numbers = [1, 2, 3]
print(add(*numbers)) # 6
# 使用**解包字典
def create_user(name, age, email):
return {"name": name, "age": age, "email": email}
user_data = {"name": "Alice", "age": 25, "email": "alice@example.com"}
user = create_user(**user_data)
print(user)
===== 3.3 变量作用域 =====
==== 3.3.1 局部变量和全局变量 ====
# 全局变量
global_var = "我在函数外"
def demo():
# 局部变量
local_var = "我在函数内"
print(local_var) # 可以访问局部变量
print(global_var) # 可以访问全局变量
demo()
print(global_var) # 可以访问全局变量
# print(local_var) # 错误!不能访问局部变量
==== 3.3.2 global 关键字 ====
在函数内修改全局变量。
counter = 0
def increment():
global counter
counter += 1
print(f"计数器: {counter}")
increment() # 计数器: 1
increment() # 计数器: 2
print(counter) # 2
==== 3.3.3 nonlocal 关键字 ====
在嵌套函数中修改外层(非全局)变量。
def outer():
x = "外部"
def inner():
nonlocal x
x = "内部修改"
print(f"inner: {x}")
inner()
print(f"outer: {x}")
outer()
# 输出:
# inner: 内部修改
# outer: 内部修改
==== 3.3.4 作用域规则(LEGB) ====
Python查找变量的顺序:
1. **L**ocal - 局部作用域
2. **E**nclosing - 嵌套函数的外层作用域
3. **G**lobal - 全局作用域
4. **B**uilt-in - 内置作用域
x = "global" # Global
def outer():
x = "enclosing" # Enclosing
def inner():
x = "local" # Local
print(x) # 输出: local
inner()
print(x) # 输出: enclosing
outer()
print(x) # 输出: global
===== 3.4 高级函数特性 =====
==== 3.4.1 函数作为对象 ====
在Python中,函数是一等对象,可以像其他对象一样传递。
def greet(name):
return f"Hello, {name}!"
# 将函数赋值给变量
say_hello = greet
print(say_hello("Alice")) # Hello, Alice!
# 函数作为参数
def execute_function(func, arg):
return func(arg)
result = execute_function(greet, "Bob")
print(result) # Hello, Bob!
# 函数作为返回值
def create_multiplier(n):
def multiplier(x):
return x * n
return multiplier
double = create_multiplier(2)
triple = create_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
==== 3.4.2 闭包 ====
闭包是引用了外部变量但在外部作用域执行的函数。
def make_power(exponent):
"""创建幂函数"""
def power(base):
return base ** exponent
return power
square = make_power(2)
cube = make_power(3)
print(square(4)) # 16
print(cube(2)) # 8
# 查看闭包信息
print(square.__closure__) # 查看闭包单元
print(square.__closure__[0].cell_contents) # 2 (exponent的值)
==== 3.4.3 匿名函数(lambda) ====
lambda用于创建小型匿名函数。
# 基本语法
square = lambda x: x ** 2
print(square(5)) # 25
# 多个参数
add = lambda x, y: x + y
print(add(3, 4)) # 7
# 默认参数
greet = lambda name, greeting="Hello": f"{greeting}, {name}!"
print(greet("Alice")) # Hello, Alice!
print(greet("Bob", "Hi")) # Hi, Bob!
=== 常用场景 ===
# 作为高阶函数的参数
numbers = [1, 2, 3, 4, 5]
# map()
squares = list(map(lambda x: x ** 2, numbers))
print(squares) # [1, 4, 9, 16, 25]
# filter()
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # [2, 4]
# sorted()的key参数
students = [("Alice", 85), ("Bob", 92), ("Charlie", 78)]
sorted_by_score = sorted(students, key=lambda x: x[1], reverse=True)
print(sorted_by_score) # [('Bob', 92), ('Alice', 85), ('Charlie', 78)]
# reduce()
from functools import reduce
product = reduce(lambda x, y: x * y, numbers)
print(product) # 120
==== 3.4.4 递归函数 ====
函数调用自身。
# 阶乘
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
print(factorial(5)) # 120
# 斐波那契数列
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print([fibonacci(i) for i in range(10)]) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
# 尾递归优化(Python不支持尾递归优化)
def factorial_tail(n, acc=1):
if n <= 1:
return acc
return factorial_tail(n - 1, n * acc)
print(factorial_tail(5)) # 120
==== 3.4.5 装饰器 ====
装饰器是修改函数行为的强大工具。
=== 基本装饰器 ===
def my_decorator(func):
def wrapper(*args, **kwargs):
print("函数执行前")
result = func(*args, **kwargs)
print("函数执行后")
return result
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
# 输出:
# 函数执行前
# Hello!
# 函数执行后
=== 带参数的装饰器 ===
def repeat(times):
"""重复执行函数的装饰器"""
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
# 输出三次: Hello, Alice!
=== 使用functools.wraps ===
from functools import wraps
def my_decorator(func):
@wraps(func) # 保留原函数的元数据
def wrapper(*args, **kwargs):
"""包装函数"""
print("执行函数...")
return func(*args, **kwargs)
return wrapper
@my_decorator
def example():
"""示例函数"""
print("Hello")
print(example.__name__) # example (不使用wraps会显示wrapper)
print(example.__doc__) # 示例函数
=== 常用装饰器示例 ===
import time
from functools import wraps
# 计时装饰器
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
elapsed = time.time() - start
print(f"{func.__name__} 执行时间: {elapsed:.4f}秒")
return result
return wrapper
# 缓存装饰器(简单的memoization)
def memoize(func):
cache = {}
@wraps(func)
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
@timer
@memoize
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
print(fib(30))
===== 3.5 函数式编程工具 =====
==== 3.5.1 map() ====
将函数应用于可迭代对象的每个元素。
numbers = [1, 2, 3, 4, 5]
# 使用lambda
squares = list(map(lambda x: x ** 2, numbers))
# 使用内置函数
lengths = list(map(len, ["apple", "banana", "cherry"]))
# 多个可迭代对象
sums = list(map(lambda x, y: x + y, [1, 2, 3], [4, 5, 6]))
print(sums) # [5, 7, 9]
==== 3.5.2 filter() ====
根据条件筛选元素。
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 筛选偶数
evens = list(filter(lambda x: x % 2 == 0, numbers))
# 筛选非空字符串
strings = ["hello", "", "world", "", "python"]
non_empty = list(filter(None, strings))
print(non_empty) # ['hello', 'world', 'python']
==== 3.5.3 reduce() ====
累积计算。
from functools import reduce
numbers = [1, 2, 3, 4, 5]
# 求和
sum_result = reduce(lambda x, y: x + y, numbers)
print(sum_result) # 15
# 求积
product = reduce(lambda x, y: x * y, numbers)
print(product) # 120
# 找最大值
maximum = reduce(lambda x, y: x if x > y else y, numbers)
print(maximum) # 5
# 带初始值
sum_with_initial = reduce(lambda x, y: x + y, numbers, 100)
print(sum_with_initial) # 115
==== 3.5.4 zip() ====
# 并行迭代
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
for name, age in zip(names, ages):
print(f"{name}: {age}")
# 创建字典
keys = ["a", "b", "c"]
values = [1, 2, 3]
d = dict(zip(keys, values))
print(d) # {'a': 1, 'b': 2, 'c': 3}
# 处理不等长序列
from itertools import zip_longest
list1 = [1, 2, 3]
list2 = ["a", "b"]
for x, y in zip_longest(list1, list2, fillvalue="-"):
print(x, y)
==== 3.5.5 其他工具函数 ====
# enumerate()
for i, char in enumerate("Python", start=1):
print(f"{i}: {char}")
# sorted()
numbers = [3, 1, 4, 1, 5, 9]
sorted_numbers = sorted(numbers)
reversed_numbers = sorted(numbers, reverse=True)
# all() / any()
print(all([True, True, False])) # False
print(any([False, True, False])) # True
print(all(x > 0 for x in [1, 2, 3])) # True
# reversed()
for char in reversed("Python"):
print(char, end="") # nohtyP
===== 3.6 代码示例 =====
==== 示例1:缓存装饰器 ====
from functools import wraps
def lru_cache(maxsize=128):
"""简单的LRU缓存装饰器"""
def decorator(func):
cache = {}
order = []
@wraps(func)
def wrapper(*args):
if args in cache:
# 移动到最近使用
order.remove(args)
order.append(args)
return cache[args]
result = func(*args)
cache[args] = result
order.append(args)
# 超出限制,删除最久未使用的
if len(cache) > maxsize:
oldest = order.pop(0)
del cache[oldest]
return result
return wrapper
return decorator
@lru_cache(maxsize=3)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(50))
==== 示例2:函数注册器 ====
class FunctionRegistry:
"""函数注册器 - 用于注册和调用函数"""
def __init__(self):
self._functions = {}
def register(self, name=None):
"""装饰器:注册函数"""
def decorator(func):
func_name = name or func.__name__
self._functions[func_name] = func
return func
return decorator
def call(self, name, *args, **kwargs):
"""调用已注册的函数"""
if name not in self._functions:
raise ValueError(f"未知函数: {name}")
return self._functions[name](*args, **kwargs)
def list_functions(self):
"""列出所有注册的函数"""
return list(self._functions.keys())
# 使用示例
registry = FunctionRegistry()
@registry.register()
def add(a, b):
return a + b
@registry.register("multiply")
def mul(a, b):
return a * b
print(registry.list_functions()) # ['add', 'multiply']
print(registry.call("add", 3, 4)) # 7
print(registry.call("multiply", 3, 4)) # 12
===== 3.7 练习题 =====
==== 练习1:计算圆的相关值 ====
编写函数,根据半径计算圆的面积和周长。
import math
def circle_properties(radius):
"""返回圆的面积和周长"""
area = math.pi * radius ** 2
circumference = 2 * math.pi * radius
return area, circumference
# 测试
r = 5
area, circumference = circle_properties(r)
print(f"半径为{r}的圆:")
print(f" 面积: {area:.2f}")
print(f" 周长: {circumference:.2f}")
==== 练习2:验证器装饰器 ====
编写装饰器验证函数参数是否为正数。
from functools import wraps
def validate_positive(func):
"""验证所有参数为正数"""
@wraps(func)
def wrapper(*args, **kwargs):
for arg in args:
if isinstance(arg, (int, float)) and arg <= 0:
raise ValueError(f"参数必须为正数,得到: {arg}")
return func(*args, **kwargs)
return wrapper
@validate_positive
def rectangle_area(width, height):
return width * height
print(rectangle_area(5, 3)) # 15
# print(rectangle_area(-5, 3)) # ValueError
==== 练习3:柯里化 ====
实现函数柯里化(将多参数函数转为单参数函数链)。
from functools import partial
def curry(func):
"""柯里化装饰器"""
def curried(*args, **kwargs):
if len(args) + len(kwargs) >= func.__code__.co_argcount:
return func(*args, **kwargs)
return partial(curried, *args, **kwargs)
return curried
@curry
def add_three(a, b, c):
return a + b + c
# 使用
print(add_three(1)(2)(3)) # 6
print(add_three(1, 2)(3)) # 6
print(add_three(1)(2, 3)) # 6
add_five = add_three(5)
print(add_five(10)(20)) # 35
==== 练习4:函数组合 ====
实现函数组合(compose)。
from functools import reduce
def compose(*functions):
"""函数组合: compose(f, g, h)(x) = f(g(h(x)))"""
def composed(x):
return reduce(lambda v, f: f(v), reversed(functions), x)
return composed
# 使用
add_one = lambda x: x + 1
double = lambda x: x * 2
square = lambda x: x ** 2
transform = compose(square, double, add_one)
# 等价于: square(double(add_one(x)))
print(transform(3)) # (3+1)*2 squared = 64
===== 本章小结 =====
本章学习了Python函数的核心概念:
* **函数定义** - def、参数、返回值、文档字符串
* **参数类型** - 位置参数、关键字参数、默认参数、*args、**kwargs
* **作用域** - 局部变量、全局变量、global、nonlocal、LEGB规则
* **高级特性** - 函数作为对象、闭包、lambda、递归
* **装饰器** - 基本装饰器、带参数的装饰器、常用装饰器模式
* **函数式编程** - map、filter、reduce、zip等工具函数
掌握函数后,你可以编写模块化、可重用的代码了。下一章将学习Python的内置数据结构。
===== 进一步阅读 =====
* [[https://docs.python.org/zh-cn/3/tutorial/controlflow.html#defining-functions|Python官方教程 - 定义函数]]
* [[https://docs.python.org/zh-cn/3/library/functools.html|functools模块文档]]