# 函数进阶 Python 函数是一等公民,理解其高级特性对于写出优雅代码至关重要。 ## 参数传递机制 ### 易错点:传值 vs 传引用 Python 使用"传对象引用"(pass by object reference),理解这一点很重要: ```python def modify_list(lst): lst.append(4) # 修改原对象 lst = [1, 2, 3, 4, 5] # 重新绑定局部变量,不影响原对象 original = [1, 2, 3] modify_list(original) print(original) # [1, 2, 3, 4] - append 生效,重新赋值无效 def modify_number(n): n = n + 1 # 创建新对象,不影响原变量 x = 10 modify_number(x) print(x) # 10 - 不变 ``` :::{warning} **可变默认参数陷阱**是 Python 最著名的陷阱之一: ```python # ❌ 错误示例 def append_to(element, to=[]): to.append(element) return to print(append_to(1)) # [1] print(append_to(2)) # [1, 2] - 不是 [2]! print(append_to(3)) # [1, 2, 3] # ✅ 正确做法 def append_to_fixed(element, to=None): if to is None: to = [] to.append(element) return to ``` ::: ## `*args` 和 `**kwargs` ### 位置参数与关键字参数 ```python def flexible_func(required, *args, keyword_only, **kwargs): """ 参数顺序: 1. 普通参数 2. *args(收集额外位置参数) 3. keyword-only 参数(*args 之后的参数) 4. **kwargs(收集额外关键字参数) """ print(f"required: {required}") print(f"args: {args}") print(f"keyword_only: {keyword_only}") print(f"kwargs: {kwargs}") flexible_func(1, 2, 3, 4, keyword_only="must specify", extra="value") # required: 1 # args: (2, 3, 4) # keyword_only: must specify # kwargs: {'extra': 'value'} ``` ### 仅位置参数(Python 3.8+) ```python def pos_only(x, y, /, z): """x 和 y 只能通过位置传递""" return x + y + z pos_only(1, 2, 3) # ✅ pos_only(1, 2, z=3) # ✅ # pos_only(x=1, y=2, z=3) # ❌ TypeError # 实际应用:避免参数名变更破坏 API def compute(x, y, /, *, precision=2): """x, y 位置参数;precision 关键字参数""" return round(x / y, precision) ``` ### 参数解包 ```python def point(x, y, z): return f"({x}, {y}, {z})" # 列表/元组解包 coords = [1, 2, 3] print(point(*coords)) # (1, 2, 3) # 字典解包 params = {'x': 1, 'y': 2, 'z': 3} print(point(**params)) # (1, 2, 3) # 合并字典(Python 3.9+) defaults = {'a': 1, 'b': 2} overrides = {'b': 3, 'c': 4} combined = {**defaults, **overrides} # {'a': 1, 'b': 3, 'c': 4} # 或使用 | 运算符 combined = defaults | overrides # Python 3.9+ ``` ## 闭包 闭包是函数式编程的基础,理解它对掌握装饰器至关重要。 ### 闭包原理 ```python def make_counter(): count = 0 # 自由变量 def counter(): nonlocal count # 声明使用外层变量 count += 1 return count return counter c1 = make_counter() c2 = make_counter() print(c1()) # 1 print(c1()) # 2 print(c2()) # 1 - 独立的计数器 # 查看闭包变量 print(c1.__closure__[0].cell_contents) # 2 ``` ### 易错点:循环中的闭包 ```python # ❌ 常见错误 funcs = [] for i in range(3): funcs.append(lambda: i) print([f() for f in funcs]) # [2, 2, 2] - 都是 2! # ✅ 解决方案1:默认参数捕获 funcs = [] for i in range(3): funcs.append(lambda i=i: i) # i=i 捕获当前值 print([f() for f in funcs]) # [0, 1, 2] # ✅ 解决方案2:使用 functools.partial from functools import partial funcs = [partial(lambda x: x, i) for i in range(3)] print([f() for f in funcs]) # [0, 1, 2] ``` ## 装饰器 ### 基本装饰器 ```python import functools import time def timer(func): """测量函数执行时间的装饰器""" @functools.wraps(func) # 保留原函数的元信息 def wrapper(*args, **kwargs): start = time.perf_counter() result = func(*args, **kwargs) end = time.perf_counter() print(f"{func.__name__} took {end - start:.4f}s") return result return wrapper @timer def slow_function(n): """一个慢函数""" time.sleep(n) return n slow_function(1) # slow_function took 1.0012s # 保留了原函数的信息 print(slow_function.__name__) # slow_function print(slow_function.__doc__) # 一个慢函数 ``` :::{important} 始终使用 `@functools.wraps` 装饰 wrapper 函数,否则原函数的 `__name__`、`__doc__` 等属性会丢失。 ::: ### 带参数的装饰器 ```python def retry(max_attempts=3, delay=1): """带重试功能的装饰器""" def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): last_exception = None for attempt in range(max_attempts): try: return func(*args, **kwargs) except Exception as e: last_exception = e print(f"Attempt {attempt + 1} failed: {e}") if attempt < max_attempts - 1: time.sleep(delay) raise last_exception return wrapper return decorator @retry(max_attempts=3, delay=0.5) def unstable_api_call(): import random if random.random() < 0.7: raise ConnectionError("Network error") return "Success" ``` ### 类装饰器 ```python class CountCalls: """统计函数调用次数的类装饰器""" def __init__(self, func): functools.update_wrapper(self, func) self.func = func self.count = 0 def __call__(self, *args, **kwargs): self.count += 1 return self.func(*args, **kwargs) @CountCalls def greet(name): return f"Hello, {name}!" print(greet("Alice")) # Hello, Alice! print(greet("Bob")) # Hello, Bob! print(greet.count) # 2 ``` ### 装饰器执行顺序 ```python def decorator_a(func): print("Applying decorator A") @functools.wraps(func) def wrapper(*args, **kwargs): print("Before A") result = func(*args, **kwargs) print("After A") return result return wrapper def decorator_b(func): print("Applying decorator B") @functools.wraps(func) def wrapper(*args, **kwargs): print("Before B") result = func(*args, **kwargs) print("After B") return result return wrapper @decorator_a @decorator_b def hello(): print("Hello!") # 应用顺序:从下到上 # Applying decorator B # Applying decorator A hello() # 调用顺序:从外到内 # Before A # Before B # Hello! # After B # After A ``` ## 函数式编程 ### map, filter, reduce ```python from functools import reduce numbers = [1, 2, 3, 4, 5] # map: 对每个元素应用函数 squared = list(map(lambda x: x**2, numbers)) # 更 Pythonic: [x**2 for x in numbers] # filter: 过滤元素 evens = list(filter(lambda x: x % 2 == 0, numbers)) # 更 Pythonic: [x for x in numbers if x % 2 == 0] # reduce: 累积计算 total = reduce(lambda acc, x: acc + x, numbers, 0) # 更 Pythonic: sum(numbers) # reduce 的实际应用场景 data = [{'name': 'Alice', 'score': 85}, {'name': 'Bob', 'score': 92}, {'name': 'Charlie', 'score': 78}] # 找出最高分 highest = reduce( lambda best, x: x if x['score'] > best['score'] else best, data ) ``` ### 偏函数 ```python from functools import partial def power(base, exponent): return base ** exponent # 创建平方函数 square = partial(power, exponent=2) # 创建立方函数 cube = partial(power, exponent=3) print(square(5)) # 25 print(cube(3)) # 27 # 实际应用:配置 API 调用 import urllib.request def fetch_url(url, timeout=10, headers=None): req = urllib.request.Request(url, headers=headers or {}) return urllib.request.urlopen(req, timeout=timeout) # 创建预配置的函数 api_fetch = partial( fetch_url, timeout=30, headers={'Authorization': 'Bearer token'} ) ``` ### operator 模块 ```python from operator import attrgetter, itemgetter, methodcaller # itemgetter: 获取字典/序列的项 students = [ {'name': 'Alice', 'grade': 85, 'age': 20}, {'name': 'Bob', 'grade': 92, 'age': 19}, {'name': 'Charlie', 'grade': 78, 'age': 21}, ] # 按成绩排序 by_grade = sorted(students, key=itemgetter('grade')) # 多字段排序 by_grade_age = sorted(students, key=itemgetter('grade', 'age')) # attrgetter: 获取对象属性 class Student: def __init__(self, name, grade): self.name = name self.grade = grade students = [Student('Alice', 85), Student('Bob', 92)] by_grade = sorted(students, key=attrgetter('grade')) # methodcaller: 调用方法 names = ['alice', 'bob', 'charlie'] upper_names = list(map(methodcaller('upper'), names)) # ['ALICE', 'BOB', 'CHARLIE'] ``` ## 最佳实践 ::::{grid} 1 :gutter: 2 :::{grid-item-card} 函数设计原则 1. **单一职责**:一个函数只做一件事 2. **合理的参数数量**:超过 5 个考虑使用配置对象 3. **避免可变默认参数**:使用 `None` 作为默认值 4. **返回一致的类型**:不要返回 `None` 或值混用 ::: :::{grid-item-card} 装饰器最佳实践 1. 始终使用 `@functools.wraps` 2. 保持装饰器简单,复杂逻辑抽取到辅助函数 3. 考虑支持同步和异步函数 4. 提供清晰的文档和示例 ::: ::::