1. 函数:Python编程的基石模块
第一次接触函数时,我把它想象成厨房里的料理机——你把食材(参数)放进去,选择功能(函数体),它就会输出处理好的食物(返回值)。这种封装思想彻底改变了我写代码的方式,从此告别了复制粘贴的混乱时代。
在真实项目开发中,函数的使用频率高得惊人。根据GitHub的统计,一个万行级的Python项目中平均包含300-500个自定义函数。学会正确使用函数,意味着你的代码将获得以下超能力:
- 避免重复代码(DRY原则)
- 实现复杂逻辑分解
- 提高可读性和维护性
- 便于团队协作开发
特别提示:本章示例全部基于Python 3.10+版本,部分特性在旧版本可能不兼容。建议新手使用VS Code或PyCharm作为开发环境,它们对函数跳转和参数提示的支持非常友好。
2. 函数定义:从语法到实战
2.1 基础定义语法
一个完整的函数定义包含四个关键部分:
python复制def 函数名(参数列表) -> 返回值类型:
"""文档字符串(docstring)"""
函数体
return 返回值
让我们拆解一个温度转换函数的实例:
python复制def celsius_to_fahrenheit(c_temp: float) -> float:
"""将摄氏温度转换为华氏温度
Args:
c_temp: 摄氏温度值,支持浮点数
Returns:
对应的华氏温度值
"""
f_temp = (c_temp * 9/5) + 32
return round(f_temp, 2) # 保留两位小数
这个简单示例已经展示了多个重要特性:
- 类型注解(: float和-> float)
- 文档字符串的标准格式
- 明确的单一职责
- 返回值处理
2.2 参数传递的奥秘
Python的参数传递机制常常让初学者困惑。本质上,它采用的是"对象引用传递"。理解这一点可以避免很多坑:
python复制def modify_list(items):
items.append('new') # 会修改原始列表
print(f"函数内ID: {id(items)}")
my_list = [1, 2, 3]
print(f"原始ID: {id(my_list)}")
modify_list(my_list)
print(my_list) # 输出: [1, 2, 3, 'new']
对于不可变对象(如整数、字符串、元组),函数内修改会创建新对象:
python复制def modify_number(num):
num += 10
print(f"函数内ID: {id(num)}")
x = 5
print(f"原始ID: {id(x)}")
modify_number(x)
print(x) # 输出仍然是5
2.3 高级参数特性
Python的函数参数系统非常灵活,但这也意味着更多需要注意的细节:
默认参数陷阱:
python复制# 错误示范
def add_item(item, items=[]):
items.append(item)
return items
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] 默认列表被共享了!
# 正确写法
def add_item_fixed(item, items=None):
if items is None:
items = []
items.append(item)
return items
可变长参数的实际应用场景:
python复制def logger(message, *args, **kwargs):
"""记录带上下文信息的日志"""
print(f"[LOG] {message}")
if args:
print("位置参数:", args)
if kwargs:
print("关键字参数:", kwargs)
logger("系统启动", "dev_mode", timeout=30)
3. 函数调用:不仅仅是执行代码
3.1 调用栈与执行流程
理解函数调用时的内存变化对调试复杂程序至关重要。我们通过一个递归例子来看调用栈:
python复制def factorial(n):
print(f"调用栈深度: {len(inspect.stack())}")
if n == 1:
return 1
return n * factorial(n-1)
import inspect
factorial(5)
输出会显示调用栈的深度变化。当递归过深时(通常超过1000层),Python会抛出RecursionError。
3.2 函数作为一等公民
Python中函数可以被当作普通变量使用,这开启了无限可能:
python复制def power(base):
def inner(exp):
return base ** exp
return inner
square = power(2)
cube = power(3)
print(square(3)) # 8
print(cube(3)) # 27
这种特性在装饰器中大放异彩:
python复制def retry(max_attempts=3):
def decorator(func):
def wrapper(*args, **kwargs):
attempts = 0
while attempts < max_attempts:
try:
return func(*args, **kwargs)
except Exception as e:
attempts += 1
print(f"尝试 {attempts} 次失败: {e}")
raise RuntimeError(f"超过最大重试次数 {max_attempts}")
return wrapper
return decorator
@retry(max_attempts=2)
def risky_operation():
import random
if random.random() < 0.7:
raise ValueError("随机失败")
return "成功"
print(risky_operation())
3.3 现代Python函数特性
Python 3.x引入了一些强大的函数特性:
类型注解的进阶用法:
python复制from typing import Union, Optional
def process_data(
data: Union[str, bytes],
encoding: Optional[str] = 'utf-8'
) -> tuple[int, dict]:
"""处理多种格式的数据
Returns:
包含处理结果状态码和元数据的元组
"""
# 实现代码...
return (200, {"size": len(data)})
海象运算符(:=)在函数中的妙用:
python复制def parse_long_string(text):
if (match := re.search(r'(\d{4})-(\d{2})-(\d{2})', text)):
year, month, day = match.groups()
return f"{year}年{month}月{day}日"
return "日期未找到"
4. 函数设计原则与性能优化
4.1 SOLID原则在函数设计中的应用
虽然SOLID是面向对象的原则,但对函数设计同样有指导意义:
-
单一职责原则:一个函数只做一件事
- 反例:
process_data_and_save_to_db() - 正例:
validate_data(),transform_data(),save_to_db()
- 反例:
-
开闭原则:通过参数化而非修改来扩展功能
python复制# 不好的写法 def calculate(operation): if operation == "add": return a + b elif operation == "sub": return a - b # 好的写法 def calculate(a, b, operator): return operator(a, b)
4.2 性能考量
局部变量访问更快:
python复制import math
def slow_circle_area(radius):
return math.pi * radius ** 2 # 每次都要查找math模块
def fast_circle_area(radius):
pi = math.pi # 局部化常量
return pi * radius ** 2
避免不必要的函数调用:
python复制# 低效写法
for item in items:
processed = expensive_processing(item)
if validate(processed):
save_to_db(processed)
# 高效写法
valid_items = [
processed for item in items
if validate(processed := expensive_processing(item))
]
save_to_db_batch(valid_items)
5. 调试与测试技巧
5.1 函数调试三板斧
-
print调试的进阶用法:
python复制def complex_calculation(x, y): print(f"输入参数: {locals()}") # 打印所有局部变量 result = x ** y print(f"计算结果: {result=}") # Python 3.8+的f-string特性 return result -
使用pdb进行交互调试:
python复制import pdb def buggy_function(data): pdb.set_trace() # 在这里暂停进入调试器 # 调试命令示例: # n(ext) - 执行下一行 # s(tep) - 进入函数调用 # l(ist) - 显示当前代码 # p - 打印变量值 -
日志记录的最佳实践:
python复制import logging logging.basicConfig(level=logging.DEBUG) def api_call(url): logging.debug(f"开始请求: {url}") try: response = requests.get(url) logging.info(f"响应状态: {response.status_code}") return response.json() except Exception as e: logging.error(f"请求失败: {str(e)}") raise
5.2 单元测试实战
pytest框架的测试示例:
python复制# test_operations.py
import pytest
def divide(a, b):
if b == 0:
raise ValueError("除数不能为零")
return a / b
@pytest.mark.parametrize("a,b,expected", [
(10, 2, 5),
(9, 3, 3),
(0, 1, 0),
])
def test_divide_normal(a, b, expected):
assert divide(a, b) == expected
def test_divide_by_zero():
with pytest.raises(ValueError) as excinfo:
divide(1, 0)
assert "除数不能为零" in str(excinfo.value)
6. 函数在真实项目中的应用模式
6.1 Web开发中的函数组织
Flask路由函数的典型结构:
python复制from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/api/users', methods=['POST'])
def create_user():
"""创建新用户API"""
try:
data = request.get_json()
# 验证输入
if not data or 'username' not in data:
return jsonify({"error": "缺少用户名"}), 400
# 业务逻辑
user = User.create(
username=data['username'],
email=data.get('email')
)
# 响应处理
return jsonify({
"id": user.id,
"username": user.username
}), 201
except Exception as e:
app.logger.error(f"创建用户失败: {str(e)}")
return jsonify({"error": "服务器内部错误"}), 500
6.2 数据处理管道
使用函数构建可复用的数据处理流程:
python复制def build_data_pipeline(*processors):
"""创建数据处理管道"""
def pipeline(data):
result = data
for processor in processors:
result = processor(result)
return result
return pipeline
# 处理器函数
def clean_text(text):
return text.strip().lower()
def remove_stopwords(text):
stopwords = {"the", "and", "a"}
return " ".join(
word for word in text.split()
if word not in stopwords
)
# 构建管道
text_pipeline = build_data_pipeline(
clean_text,
remove_stopwords,
str.title
)
processed = text_pipeline(" THE quick brown fox ")
print(processed) # "Quick Brown Fox"
6.3 异步函数实战
现代Python中async/await的典型用法:
python复制import aiohttp
import asyncio
async def fetch_url(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
async def main():
urls = [
"https://example.com",
"https://python.org",
"https://github.com"
]
tasks = [fetch_url(url) for url in urls]
results = await asyncio.gather(*tasks)
print(f"获取了 {len(results)} 个页面的数据")
# Python 3.7+
asyncio.run(main())
7. 函数设计的常见误区与解决方案
7.1 参数设计陷阱
过度使用布尔参数:
python复制# 不好的设计
def process_data(data, normalize=False, validate=False, log=False):
if normalize:
# 归一化处理...
if validate:
# 验证数据...
if log:
# 记录日志...
# 好的设计
def normalize_data(data):
# 专门的处理函数
...
def validate_data(data):
# 专门的验证函数
...
# 组合使用
data = normalize_data(raw_data)
if validate_data(data):
save_data(data)
可变默认参数的替代方案:
python复制# 危险的做法
def append_to(element, target=[]):
target.append(element)
return target
# 安全的替代方案
def append_to_fixed(element, target=None):
if target is None:
target = []
target.append(element)
return target
7.2 返回值设计的艺术
返回一致性:
python复制# 不一致的返回类型
def find_user(user_id):
if user := db.get(user_id):
return user # User对象
return None # 或者返回False/空字典等
# 一致的返回类型
def find_user_improved(user_id):
user = db.get(user_id)
return {
"found": user is not None,
"user": user if user else None,
"message": "用户未找到" if not user else None
}
多返回值处理:
python复制# 返回元组的常见模式
def analyze_text(text):
word_count = len(text.split())
char_count = len(text)
return word_count, char_count
# 调用时明确命名
words, chars = analyze_text("Hello world")
print(f"单词数: {words}, 字符数: {chars}")
# 或者使用命名元组
from collections import namedtuple
AnalysisResult = namedtuple('AnalysisResult', ['words', 'chars'])
def analyze_text_named(text):
return AnalysisResult(
words=len(text.split()),
chars=len(text)
)
result = analyze_text_named("Hello world")
print(result.words, result.chars)
8. 函数进阶:闭包与装饰器实战
8.1 闭包的实际应用
状态保持的优雅方案:
python复制def make_counter():
count = 0 # 闭包捕获的变量
def counter():
nonlocal count # 必须声明nonlocal才能修改
count += 1
return count
return counter
c1 = make_counter()
print(c1(), c1(), c1()) # 1, 2, 3
c2 = make_counter()
print(c2(), c1(), c2()) # 1, 4, 2
配置工厂模式:
python复制def configure_logger(level):
def logger(message):
if level == "DEBUG":
print(f"[DEBUG] {message}")
elif level == "WARNING":
print(f"⚠️ {message}")
elif level == "ERROR":
print(f"❌ {message}")
return logger
debug_log = configure_logger("DEBUG")
error_log = configure_logger("ERROR")
debug_log("这是一个调试信息")
error_log("严重错误发生!")
8.2 装饰器的高级模式
带参数的装饰器:
python复制def retry(max_attempts=3, delay=1):
def decorator(func):
import time
def wrapper(*args, **kwargs):
attempts = 0
while attempts < max_attempts:
try:
return func(*args, **kwargs)
except Exception as e:
attempts += 1
if attempts == max_attempts:
raise
time.sleep(delay)
return wrapper
return decorator
@retry(max_attempts=5, delay=2)
def unreliable_api_call():
import random
if random.random() < 0.8:
raise ConnectionError("API调用失败")
return "成功"
类装饰器:
python复制class MeasureTime:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
import time
start = time.perf_counter()
result = self.func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{self.func.__name__} 执行时间: {elapsed:.4f}秒")
return result
@MeasureTime
def long_running_task(n):
return sum(i * i for i in range(n))
result = long_running_task(1000000)
9. 类型提示与静态检查
9.1 现代类型提示系统
基础类型注解:
python复制def greet(name: str, times: int = 1) -> str:
return "\n".join([f"Hello {name}!"] * times)
复杂类型支持:
python复制from typing import List, Dict, Tuple, Optional, Union
def process_items(
items: List[Union[str, int]],
config: Dict[str, float],
threshold: Optional[float] = None
) -> Tuple[bool, int]:
# 实现代码...
return (True, len(items))
自定义类型:
python复制from typing import NewType
UserId = NewType('UserId', int)
PostId = NewType('PostId', int)
def get_user(user_id: UserId) -> dict:
# 实现代码...
return {}
# 这样调用会通过类型检查
user = get_user(UserId(12345))
# 这样会引发类型检查警告
invalid = get_user(12345) # 期望UserId类型,得到的是int
9.2 mypy静态类型检查
安装与基本使用:
bash复制pip install mypy
mypy your_script.py
配置mypy.ini文件示例:
ini复制[mypy]
python_version = 3.10
warn_return_any = True
warn_unused_configs = True
disallow_untyped_defs = True
常见类型错误模式:
python复制# 错误1:缺少返回类型注解
def add(a, b): # 错误:函数缺少返回类型注解
return a + b
# 错误2:不一致的返回类型
def get_status(ok: bool) -> str:
if ok:
return "success"
return 404 # 错误:返回类型不一致
# 错误3:参数类型不匹配
def double(x: int) -> int:
return x * 2
double("2") # 错误:参数类型不匹配
10. 函数性能优化进阶
10.1 缓存技术应用
functools.lru_cache:
python复制from functools import lru_cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
# 第一次计算会执行递归
print(fibonacci(50)) # 非常快速,因为使用了缓存
# 查看缓存命中情况
print(fibonacci.cache_info())
自定义缓存装饰器:
python复制def timed_cache(seconds):
def decorator(func):
cache = {}
import time
def wrapper(*args):
now = time.time()
if args in cache:
value, timestamp = cache[args]
if now - timestamp < seconds:
return value
result = func(*args)
cache[args] = (result, now)
return result
return wrapper
return decorator
@timed_cache(seconds=10)
def get_current_price(stock_id):
print(f"真实查询股票 {stock_id} 价格...")
return 100 + hash(stock_id) % 50 # 模拟价格
10.2 向量化运算
避免循环的数学运算:
python复制import numpy as np
# 低效的Python循环
def slow_dot_product(a, b):
result = 0
for x, y in zip(a, b):
result += x * y
return result
# 高效的向量化运算
def fast_dot_product(a, b):
return np.dot(np.array(a), np.array(b))
# 性能对比
a = list(range(1000000))
b = list(range(1000000, 0, -1))
%timeit slow_dot_product(a, b) # 约200ms
%timeit fast_dot_product(a, b) # 约5ms
Numba加速:
python复制from numba import jit
@jit(nopython=True)
def monte_carlo_pi(n_samples):
count = 0
for _ in range(n_samples):
x = np.random.random()
y = np.random.random()
if x**2 + y**2 < 1:
count += 1
return 4 * count / n_samples
# 第一次运行会编译函数
print(monte_carlo_pi(1000000)) # 约3.141
11. 函数式编程技巧
11.1 高阶函数应用
map/filter/reduce的现代替代:
python复制numbers = [1, 2, 3, 4, 5]
# 传统方式
squares = list(map(lambda x: x**2, numbers))
evens = list(filter(lambda x: x % 2 == 0, numbers))
# 更Pythonic的方式
squares = [x**2 for x in numbers]
evens = [x for x in numbers if x % 2 == 0]
# reduce的替代方案
from functools import reduce
product = reduce(lambda x, y: x * y, numbers)
# 通常更清晰的写法
import math
product = math.prod(numbers) # Python 3.8+
偏函数应用:
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(5)) # 125
11.2 生成器函数
yield的高级用法:
python复制def sensor_simulator():
import random
while True:
yield {
"temp": random.uniform(18, 25),
"humidity": random.uniform(30, 70),
"timestamp": time.time()
}
# 使用示例
sensor = sensor_simulator()
for _ in range(3):
print(next(sensor))
协程与yield from:
python复制def sub_generator():
yield "开始子生成器"
yield "子生成器处理中"
return "子生成器结果"
def main_generator():
yield "主生成器开始"
result = yield from sub_generator()
yield f"获取到子生成器结果: {result}"
yield "主生成器结束"
for item in main_generator():
print(item)
12. 函数与面向对象的结合
12.1 类中的方法设计
方法类型对比:
python复制class MyClass:
def instance_method(self):
print(f"实例方法调用,self={self}")
@classmethod
def class_method(cls):
print(f"类方法调用,cls={cls}")
@staticmethod
def static_method():
print("静态方法调用,没有self/cls")
# 使用区别
obj = MyClass()
obj.instance_method() # 自动传入self
MyClass.class_method() # 自动传入cls
MyClass.static_method() # 不自动传参
property装饰器:
python复制class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
"""半径属性,带验证"""
return self._radius
@radius.setter
def radius(self, value):
if value <= 0:
raise ValueError("半径必须为正数")
self._radius = value
@property
def area(self):
"""计算属性,只读"""
return 3.14 * self._radius ** 2
c = Circle(5)
print(c.area) # 78.5
c.radius = 10
print(c.area) # 314.0
12.2 可调用对象
__call__方法实现:
python复制class Adder:
def __init__(self, base):
self.base = base
def __call__(self, x):
return self.base + x
add5 = Adder(5)
print(add5(3)) # 8
print(callable(add5)) # True
函数与对象的结合:
python复制class APIEndpoint:
def __init__(self, base_url):
self.base_url = base_url
def __call__(self, path):
import requests
url = f"{self.base_url}/{path}"
return requests.get(url).json()
# 创建实例
api = APIEndpoint("https://api.example.com")
# 像函数一样调用
data = api("users/123")
13. 元编程与动态函数
13.1 动态创建函数
使用types.FunctionType:
python复制import types
def create_function(name, arg_names, code):
# 构建代码对象
code_obj = compile(code, "<string>", "exec")
# 创建函数对象
globals_dict = {}
exec(code_obj, globals_dict)
return types.FunctionType(
globals_dict[name].__code__,
globals(),
name=name,
argdefs=None,
closure=None
)
# 动态创建函数
dynamic_func = create_function(
"say_hello",
["name"],
"def say_hello(name):\n return f'Hello {name}!'"
)
print(dynamic_func("World")) # Hello World!
lambda的限制与替代:
python复制# lambda只能包含单个表达式
square = lambda x: x ** 2
# 更复杂的逻辑需要使用普通函数
def complex_operation(x):
if x < 0:
return 0
result = x ** 2
if result > 100:
return 100
return result
# 或者使用eval(谨慎使用!)
operation = eval("lambda x: 0 if x < 0 else min(x**2, 100)")
13.2 函数内省
获取函数信息:
python复制def example(a: int, b: str = "hello") -> float:
"""示例函数"""
return float(a + len(b))
# 获取签名信息
import inspect
sig = inspect.signature(example)
print(sig) # (a: int, b: str = 'hello') -> float
# 参数详细信息
for name, param in sig.parameters.items():
print(f"{name}: {param.annotation} = {param.default}")
# 返回类型
print(sig.return_annotation) # <class 'float'>
# 源代码
print(inspect.getsource(example))
动态修改函数:
python复制def original():
return 42
def wrapper(func):
def inner():
print("函数被调用")
result = func()
print(f"返回结果: {result}")
return result
return inner
# 替换函数实现
original = wrapper(original)
print(original())
# 输出:
# 函数被调用
# 返回结果: 42
# 42
14. 函数安全与沙箱
14.1 安全执行不可信代码
限制全局命名空间:
python复制def safe_eval(expr):
allowed_globals = {
"__builtins__": {
"abs": abs,
"max": max,
"min": min,
"pow": pow,
"round": round,
"sum": sum,
}
}
return eval(expr, allowed_globals, {})
print(safe_eval("sum([1, 2, 3])")) # 6
print(safe_eval("__import__('os').system('ls')")) # 安全错误
使用ast检查语法树:
python复制import ast
class SafeVisitor(ast.NodeVisitor):
allowed_nodes = {
ast.Expression, ast.Call, ast.Name, ast.Constant,
ast.List, ast.Tuple, ast.Dict, ast.BinOp, ast.UnaryOp,
ast.Add, ast.Sub, ast.Mult, ast.Div, ast.USub
}
def generic_visit(self, node):
if type(node) not in self.allowed_nodes:
raise ValueError(f"禁止的语法: {type(node).__name__}")
super().generic_visit(node)
def safe_eval_ast(expr):
tree = ast.parse(expr, mode='eval')
SafeVisitor().visit(tree)
return eval(compile(tree, '<string>', 'eval'), {}, {})
print(safe_eval_ast("1 + 2 * 3")) # 7
print(safe_eval_ast("__import__('os')")) # 引发ValueError
14.2 资源限制
设置执行超时:
python复制import signal
def timeout_handler(signum, frame):
raise TimeoutError("函数执行超时")
def run_with_timeout(func, args=(), kwargs={}, timeout=1):
# 设置信号处理器
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(timeout)
try:
result = func(*args, **kwargs)
signal.alarm(0) # 取消定时器
return result
except Exception as e:
signal.alarm(0)
raise
def long_running_task():
import time
time.sleep(2)
return "完成"
try:
print(run_with_timeout(long_running_task, timeout=1))
except TimeoutError as e:
print(e) # 函数执行超时
内存限制(Linux):
python复制import resource
def set_memory_limit(limit_mb):
soft, hard = resource.getrlimit(resource.RLIMIT_AS)
new_limit = limit_mb * 1024 * 1024
resource.setrlimit(resource.RLIMIT_AS, (new_limit, hard))
def memory_hungry_task():
data = []
for i in range(1000000):
data.append(" " * 1024) # 1MB每项
return len(data)
try:
set_memory_limit(50) # 限制50MB
memory_hungry_task()
except MemoryError:
print("内存超出限制")
15. 函数文档与测试的最佳实践
15.1 文档字符串标准
Google风格示例:
python复制def calculate_stats(data, method='mean'):
"""计算数据集的统计指标
支持多种统计方法的计算,包括均值、中位数和众数。
Args:
data: 数值型数据列表或数组
method: 统计方法,可选 'mean'、'median' 或 'mode'
Returns:
计算得到的统计值
Raises:
ValueError: 当输入数据为空或方法不支持时
TypeError: 当输入数据包含非数值类型时
Examples:
>>> calculate_stats([1, 2, 3, 4])
2.5
>>> calculate_stats([1, 1, 2, 3], 'mode')
1
"""
if not data:
raise ValueError("输入数据不能为空")
if method == 'mean':
return sum(data) / len(data)
elif method == 'median':
sorted_data = sorted(data)
n = len(sorted_data)
mid = n // 2
if n % 2 == 0:
return (sorted_data[mid-1] + sorted_data[mid]) / 2
return sorted_data[mid]
elif method == 'mode':
from collections import Counter
return Counter(data).most_common(1)[0][0]
else:
raise ValueError(f"不支持的方法: {method}")
15.2 文档测试
doctest示例:
python复制def factorial(n):
"""计算阶乘
>>> factorial(0)
1
>>> factorial(1)
1
>>> factorial(5)
120
>>> factorial(-1)
Traceback (most recent call last):
...
ValueError: n必须为非负整数
"""
if n < 0:
raise ValueError("n必须为非负整数")
return 1 if n <= 1 else n * factorial(n - 1)
if __name__ == "__main__":
import doctest
doctest.testmod()
pytest文档测试:
python复制def add(a, b):
"""两个数相加
Examples:
>>> add(2, 3)
5
>>> add(-1, 1)
0
>>> add(0.5, 0.5)
1.0
"""
return a + b
# pytest会默认执行文档测试
# 也可以显式指定:pytest --doctest-modules
16. 函数版本兼容性设计
16.1 向后兼容策略
参数兼容处理:
python复制def process_data(data, encoding='utf-8', **kwargs):
"""处理数据,保持向后兼容
Args:
data: 输入数据
encoding: 文本编码方式
**kwargs: 未来扩展参数
Note:
- v1.0: 只有data参数
- v1.1: 增加encoding参数
- v1.2: 增加kwargs以支持未来扩展
"""
# 处理旧版本调用方式
if isinstance(data, tuple) and len(data) == 2:
warnings.warn("旧式调用方式已弃用", DeprecationWarning)
data, encoding = data
# 实际处理逻辑
if isinstance(data, bytes):
return data.decode(encoding)
return str(data)
版本分支逻辑:
python复制def draw_chart(data, style='modern'):
"""绘制图表,支持多种样式版本"""
if style == 'legacy':
return _draw_legacy_chart(data)
elif style == 'modern':
return _draw_modern_chart(data)
elif style == 'experimental':
return _draw_experimental_chart(data)
else:
raise ValueError(f"未知样式: {style}")
def _draw_legacy_chart(data):
"""v1.0版本的图表绘制"""
print("使用传统样式绘制...")
def _draw_modern_chart(data):
"""v2.0版本的图表绘制"""
print("使用现代样式绘制...")
def _draw_experimental_chart(data):
"""开发中的新样式"""
print("使用实验性样式绘制...")
16.2 弃用警告
自定义弃用装饰器:
python复制import warnings
from functools import wraps
def deprecated(reason=None, version=None, replacement=None):
def decorator(func):
@wraps(func)
def
