backup materials and knowledge-base docs
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
"""
|
||||
调试流程示例
|
||||
|
||||
这个示例展示了完整的调试流程,从发现问题到解决问题。
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 1:IndexError
|
||||
# ============================================
|
||||
|
||||
def get_item(items, index):
|
||||
"""
|
||||
问题:直接访问索引可能导致 IndexError
|
||||
|
||||
错误现象:
|
||||
IndexError: list index out of range
|
||||
"""
|
||||
# ❌ 有问题的代码
|
||||
# return items[index]
|
||||
|
||||
# ✅ 修复后的代码
|
||||
if 0 <= index < len(items):
|
||||
return items[index]
|
||||
else:
|
||||
logger.warning(f"索引 {index} 超出范围 [0, {len(items)})")
|
||||
return None
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 2:TypeError - 字符串拼接
|
||||
# ============================================
|
||||
|
||||
def format_message(name, count):
|
||||
"""
|
||||
问题:尝试拼接字符串和数字
|
||||
|
||||
错误现象:
|
||||
TypeError: can only concatenate str (not "int") to str
|
||||
"""
|
||||
# ❌ 有问题的代码
|
||||
# return name + ": " + count
|
||||
|
||||
# ✅ 修复后的代码
|
||||
return f"{name}: {count}"
|
||||
# 或
|
||||
# return name + ": " + str(count)
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 3:KeyError
|
||||
# ============================================
|
||||
|
||||
def get_user_info(users, user_id):
|
||||
"""
|
||||
问题:直接访问字典中可能不存在的键
|
||||
|
||||
错误现象:
|
||||
KeyError: 'user_123'
|
||||
"""
|
||||
# ❌ 有问题的代码
|
||||
# return users[user_id]
|
||||
|
||||
# ✅ 修复后的代码 - 方法1:使用 get()
|
||||
return users.get(user_id, None)
|
||||
|
||||
# ✅ 修复后的代码 - 方法2:检查键是否存在
|
||||
# if user_id in users:
|
||||
# return users[user_id]
|
||||
# return None
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 4:AttributeError - None 对象
|
||||
# ============================================
|
||||
|
||||
def process_data(data_provider):
|
||||
"""
|
||||
问题:data_provider 可能返回 None
|
||||
|
||||
错误现象:
|
||||
AttributeError: 'NoneType' object has no attribute 'process'
|
||||
"""
|
||||
data = data_provider.get_data()
|
||||
|
||||
# ❌ 有问题的代码
|
||||
# return data.process()
|
||||
|
||||
# ✅ 修复后的代码
|
||||
if data is not None:
|
||||
return data.process()
|
||||
else:
|
||||
logger.error("数据为 None,无法处理")
|
||||
return None
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 5:修改正在迭代的列表
|
||||
# ============================================
|
||||
|
||||
def remove_even_numbers(numbers):
|
||||
"""
|
||||
问题:迭代时修改列表导致跳过元素
|
||||
|
||||
错误现象:
|
||||
某些偶数没有被移除
|
||||
"""
|
||||
# ❌ 有问题的代码
|
||||
# for num in numbers:
|
||||
# if num % 2 == 0:
|
||||
# numbers.remove(num)
|
||||
# return numbers
|
||||
|
||||
# ✅ 修复后的代码 - 方法1:列表推导式
|
||||
return [num for num in numbers if num % 2 != 0]
|
||||
|
||||
# ✅ 修复后的代码 - 方法2:使用副本
|
||||
# for num in numbers[:]:
|
||||
# if num % 2 == 0:
|
||||
# numbers.remove(num)
|
||||
# return numbers
|
||||
|
||||
|
||||
# ============================================
|
||||
# 调试技巧示例
|
||||
# ============================================
|
||||
|
||||
def debug_with_logging(data):
|
||||
"""
|
||||
使用日志追踪问题
|
||||
"""
|
||||
logger.debug(f"输入数据: {data}")
|
||||
|
||||
# 步骤 1
|
||||
processed = step1(data)
|
||||
logger.debug(f"步骤1结果: {processed}")
|
||||
|
||||
# 步骤 2
|
||||
result = step2(processed)
|
||||
logger.debug(f"步骤2结果: {result}")
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def step1(data):
|
||||
"""模拟步骤1"""
|
||||
return [x * 2 for x in data]
|
||||
|
||||
|
||||
def step2(data):
|
||||
"""模拟步骤2"""
|
||||
return sum(data)
|
||||
|
||||
|
||||
# ============================================
|
||||
# 异常处理示例
|
||||
# ============================================
|
||||
|
||||
def safe_divide(a, b):
|
||||
"""
|
||||
正确的异常处理模式
|
||||
"""
|
||||
try:
|
||||
result = a / b
|
||||
logger.info(f"{a} / {b} = {result}")
|
||||
return result
|
||||
except ZeroDivisionError:
|
||||
logger.error(f"除数不能为零: {b}")
|
||||
return None
|
||||
except TypeError as e:
|
||||
logger.error(f"类型错误: {e}")
|
||||
return None
|
||||
|
||||
|
||||
# ============================================
|
||||
# 使用断言进行调试
|
||||
# ============================================
|
||||
|
||||
def calculate_discount(price, discount_rate):
|
||||
"""
|
||||
使用断言验证假设
|
||||
"""
|
||||
# 断言:价格应该是正数
|
||||
assert price > 0, f"价格应该是正数,实际: {price}"
|
||||
|
||||
# 断言:折扣率应该在 0-1 之间
|
||||
assert 0 <= discount_rate <= 1, f"折扣率应该在 0-1 之间,实际: {discount_rate}"
|
||||
|
||||
discounted_price = price * (1 - discount_rate)
|
||||
|
||||
# 断言:折扣后的价格应该小于原价
|
||||
assert discounted_price <= price, "折扣价格应该小于原价"
|
||||
|
||||
return discounted_price
|
||||
|
||||
|
||||
# ============================================
|
||||
# 测试代码
|
||||
# ============================================
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 50)
|
||||
print("调试示例")
|
||||
print("=" * 50)
|
||||
|
||||
# 测试 get_item
|
||||
print("\n1. 测试 get_item:")
|
||||
items = ['a', 'b', 'c']
|
||||
print(f"get_item(items, 1) = {get_item(items, 1)}")
|
||||
print(f"get_item(items, 10) = {get_item(items, 10)}")
|
||||
|
||||
# 测试 format_message
|
||||
print("\n2. 测试 format_message:")
|
||||
print(f"format_message('Count', 42) = {format_message('Count', 42)}")
|
||||
|
||||
# 测试 get_user_info
|
||||
print("\n3. 测试 get_user_info:")
|
||||
users = {'user_1': 'Alice', 'user_2': 'Bob'}
|
||||
print(f"get_user_info(users, 'user_1') = {get_user_info(users, 'user_1')}")
|
||||
print(f"get_user_info(users, 'user_999') = {get_user_info(users, 'user_999')}")
|
||||
|
||||
# 测试 remove_even_numbers
|
||||
print("\n4. 测试 remove_even_numbers:")
|
||||
numbers = [1, 2, 3, 4, 5, 6]
|
||||
print(f"原始: {numbers}")
|
||||
print(f"结果: {remove_even_numbers(numbers)}")
|
||||
|
||||
# 测试 safe_divide
|
||||
print("\n5. 测试 safe_divide:")
|
||||
print(f"safe_divide(10, 2) = {safe_divide(10, 2)}")
|
||||
print(f"safe_divide(10, 0) = {safe_divide(10, 0)}")
|
||||
|
||||
# 测试 calculate_discount
|
||||
print("\n6. 测试 calculate_discount:")
|
||||
print(f"calculate_discount(100, 0.2) = {calculate_discount(100, 0.2)}")
|
||||
@@ -0,0 +1,400 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Shell 脚本调试流程示例
|
||||
# 展示常见的 Bash 脚本错误和调试方法
|
||||
#
|
||||
|
||||
# ============================================
|
||||
# 调试设置
|
||||
# ============================================
|
||||
|
||||
# 取消注释以启用调试模式
|
||||
# set -x # 打印每个命令
|
||||
# set -e # 错误时退出
|
||||
# set -u # 未定义变量报错
|
||||
# set -o pipefail # 管道中任何命令失败则失败
|
||||
|
||||
# 或组合使用
|
||||
# set -xeuo pipefail # 严格模式
|
||||
|
||||
|
||||
# ============================================
|
||||
# 错误处理函数
|
||||
# ============================================
|
||||
|
||||
# 错误退出函数
|
||||
die() {
|
||||
local message="$1"
|
||||
local exit_code="${2:-1}"
|
||||
echo "Error: $message" >&2
|
||||
exit "$exit_code"
|
||||
}
|
||||
|
||||
# 记录函数
|
||||
log() {
|
||||
local level="$1"
|
||||
shift
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*" >&2
|
||||
}
|
||||
|
||||
log_info() { log "INFO" "$@"; }
|
||||
log_warn() { log "WARN" "$@"; }
|
||||
log_error() { log "ERROR" "$@"; }
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 1:未引用的变量
|
||||
# ============================================
|
||||
|
||||
# 错误示例:变量未引用
|
||||
demo_unquoted_variable() {
|
||||
echo "=== 问题 1:未引用的变量 ==="
|
||||
|
||||
local name="John Doe"
|
||||
|
||||
# 错误:变量未引用,空值会导致语法错误
|
||||
# if [ $name = "John Doe" ]; then
|
||||
# echo "Match"
|
||||
# fi
|
||||
|
||||
# 正确:始终引用变量
|
||||
if [ "$name" = "John Doe" ]; then
|
||||
log_info "变量匹配: $name"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 2:命令失败继续执行
|
||||
# ============================================
|
||||
|
||||
demo_command_failure() {
|
||||
echo "=== 问题 2:命令失败继续执行 ==="
|
||||
|
||||
# 错误:cd 失败后继续执行
|
||||
# cd /nonexistent_directory
|
||||
# rm -rf file.txt # 会删除当前目录的文件!
|
||||
|
||||
# 正确:检查命令是否成功
|
||||
cd /tmp || die "无法切换到 /tmp 目录"
|
||||
log_info "成功切换到目录: $(pwd)"
|
||||
|
||||
cd - > /dev/null || true
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 3:循环中的变量作用域(管道问题)
|
||||
# ============================================
|
||||
|
||||
demo_pipeline_scope() {
|
||||
echo "=== 问题 3:管道中的变量作用域 ==="
|
||||
|
||||
local count=0
|
||||
|
||||
# 错误:管道创建子 shell,外部变量不改变
|
||||
# echo -e "1\n2\n3" | while read line; do
|
||||
# count=$((count + 1))
|
||||
# done
|
||||
# echo "Count: $count" # 输出 0
|
||||
|
||||
# 正确:使用重定向
|
||||
while read line; do
|
||||
count=$((count + 1))
|
||||
done < <(echo -e "1\n2\n3")
|
||||
log_info "计数结果: $count"
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 4:数组操作
|
||||
# ============================================
|
||||
|
||||
demo_array_operations() {
|
||||
echo "=== 问题 4:数组操作 ==="
|
||||
|
||||
local fruits=("apple" "banana" "cherry")
|
||||
|
||||
# 错误的数组访问
|
||||
# echo $fruits[1] # 输出 apple[1]
|
||||
|
||||
# 正确的数组访问
|
||||
log_info "第一个元素: ${fruits[0]}"
|
||||
log_info "第二个元素: ${fruits[1]}"
|
||||
log_info "所有元素: ${fruits[@]}"
|
||||
log_info "数组长度: ${#fruits[@]}"
|
||||
|
||||
# 遍历数组
|
||||
for fruit in "${fruits[@]}"; do
|
||||
log_info "水果: $fruit"
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 5:字符串比较
|
||||
# ============================================
|
||||
|
||||
demo_string_comparison() {
|
||||
echo "=== 问题 5:字符串比较 ==="
|
||||
|
||||
local name="John"
|
||||
|
||||
# 错误:数字比较使用 =
|
||||
# if [ $age = 18 ]; then
|
||||
|
||||
# 正确:字符串比较
|
||||
if [[ "$name" == "John" ]]; then
|
||||
log_info "字符串匹配"
|
||||
fi
|
||||
|
||||
# 正确:数字比较
|
||||
local age=18
|
||||
if [ "$age" -eq 18 ]; then
|
||||
log_info "数字匹配"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 问题 6:算术运算
|
||||
# ============================================
|
||||
|
||||
demo_arithmetic() {
|
||||
echo "=== 问题 6:算术运算 ==="
|
||||
|
||||
local a=10
|
||||
local b=5
|
||||
|
||||
# 错误:使用 let 或 $(())
|
||||
# result = a + b # 这是命令调用
|
||||
|
||||
# 正确的算术运算
|
||||
local result=$((a + b))
|
||||
log_info "加法: $a + $b = $result"
|
||||
|
||||
result=$((a - b))
|
||||
log_info "减法: $a - $b = $result"
|
||||
|
||||
result=$((a * b))
|
||||
log_info "乘法: $a * $b = $result"
|
||||
|
||||
result=$((a / b))
|
||||
log_info "除法: $a / $b = $result"
|
||||
|
||||
# 使用 let
|
||||
let result=a+b
|
||||
log_info "let 加法: $result"
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 参数验证
|
||||
# ============================================
|
||||
|
||||
validate_arguments() {
|
||||
echo "=== 参数验证 ==="
|
||||
|
||||
# 检查参数数量
|
||||
if [ $# -lt 2 ]; then
|
||||
die "用法: $0 <文件> <目录>" 2
|
||||
fi
|
||||
|
||||
local file="$1"
|
||||
local dir="$2"
|
||||
|
||||
# 检查文件存在
|
||||
if [ ! -f "$file" ]; then
|
||||
die "文件不存在: $file" 3
|
||||
fi
|
||||
|
||||
# 检查目录存在
|
||||
if [ ! -d "$dir" ]; then
|
||||
die "目录不存在: $dir" 4
|
||||
fi
|
||||
|
||||
log_info "参数验证通过"
|
||||
log_info "文件: $file"
|
||||
log_info "目录: $dir"
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 使用 trap 进行清理
|
||||
# ============================================
|
||||
|
||||
demo_trap() {
|
||||
echo "=== 使用 trap 进行清理 ==="
|
||||
|
||||
# 设置清理函数
|
||||
cleanup() {
|
||||
log_info "执行清理操作..."
|
||||
# 这里可以执行清理操作
|
||||
}
|
||||
|
||||
# 捕获退出信号
|
||||
trap cleanup EXIT
|
||||
|
||||
# 捕获错误信号
|
||||
trap 'log_error "发生错误,行号: $LINENO"' ERR
|
||||
|
||||
# 捕获中断信号
|
||||
trap 'log_warn "脚本被中断"; cleanup; exit 130' INT
|
||||
|
||||
log_info "执行一些操作..."
|
||||
# 模拟操作
|
||||
sleep 1
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 调试技巧示例
|
||||
# ============================================
|
||||
|
||||
demo_debugging() {
|
||||
echo "=== 调试技巧 ==="
|
||||
|
||||
# 1. 使用 echo 调试
|
||||
local value="test"
|
||||
echo "[DEBUG] value = $value" >&2
|
||||
|
||||
# 2. 使用 printf 格式化输出
|
||||
printf "[DEBUG] Count: %d, Name: %s\n" 42 "John" >&2
|
||||
|
||||
# 3. 检查变量是否设置
|
||||
if [ -z "${unset_var+x}" ]; then
|
||||
log_warn "变量 unset_var 未设置"
|
||||
fi
|
||||
|
||||
# 4. 显示调用栈
|
||||
log_info "调用栈:"
|
||||
local i=0
|
||||
while caller $i; do
|
||||
((i++))
|
||||
done 2>/dev/null || true
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 文件操作错误处理
|
||||
# ============================================
|
||||
|
||||
demo_file_operations() {
|
||||
echo "=== 文件操作错误处理 ==="
|
||||
|
||||
local tmpfile=$(mktemp) || die "无法创建临时文件"
|
||||
|
||||
# 确保文件被删除
|
||||
trap "rm -f '$tmpfile'" EXIT
|
||||
|
||||
# 写入文件
|
||||
echo "Test content" > "$tmpfile" || die "无法写入文件: $tmpfile"
|
||||
|
||||
# 读取文件
|
||||
local content
|
||||
content=$(cat "$tmpfile") || die "无法读取文件: $tmpfile"
|
||||
|
||||
log_info "文件内容: $content"
|
||||
|
||||
# trap 会自动清理
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 带重试的操作
|
||||
# ============================================
|
||||
|
||||
demo_retry() {
|
||||
echo "=== 带重试的操作 ==="
|
||||
|
||||
local max_attempts=3
|
||||
local attempt=1
|
||||
|
||||
while [ $attempt -le $max_attempts ]; do
|
||||
log_info "尝试 $attempt/$max_attempts..."
|
||||
|
||||
# 模拟可能失败的操作
|
||||
if [ $attempt -eq 2 ]; then
|
||||
log_info "成功!"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_warn "失败,重试..."
|
||||
((attempt++))
|
||||
sleep 1
|
||||
done
|
||||
|
||||
log_error "所有尝试都失败了"
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 检查依赖
|
||||
# ============================================
|
||||
|
||||
check_dependencies() {
|
||||
echo "=== 检查依赖 ==="
|
||||
|
||||
local required_commands=("curl" "jq" "git")
|
||||
|
||||
for cmd in "${required_commands[@]}"; do
|
||||
if ! command -v "$cmd" &> /dev/null; then
|
||||
log_error "缺少依赖: $cmd"
|
||||
return 1
|
||||
fi
|
||||
log_info "✓ $cmd 可用"
|
||||
done
|
||||
|
||||
log_info "所有依赖都已满足"
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 主函数
|
||||
# ============================================
|
||||
|
||||
main() {
|
||||
echo "============================================"
|
||||
echo "Shell 脚本调试示例"
|
||||
echo "============================================"
|
||||
|
||||
# 运行各个示例
|
||||
demo_unquoted_variable
|
||||
echo ""
|
||||
|
||||
demo_command_failure
|
||||
echo ""
|
||||
|
||||
demo_pipeline_scope
|
||||
echo ""
|
||||
|
||||
demo_array_operations
|
||||
echo ""
|
||||
|
||||
demo_string_comparison
|
||||
echo ""
|
||||
|
||||
demo_arithmetic
|
||||
echo ""
|
||||
|
||||
demo_trap
|
||||
echo ""
|
||||
|
||||
demo_debugging
|
||||
echo ""
|
||||
|
||||
demo_file_operations
|
||||
echo ""
|
||||
|
||||
demo_retry
|
||||
echo ""
|
||||
|
||||
check_dependencies
|
||||
echo ""
|
||||
|
||||
log_info "所有示例执行完成"
|
||||
}
|
||||
|
||||
# 运行主函数
|
||||
main "$@"
|
||||
@@ -0,0 +1,305 @@
|
||||
"""
|
||||
错误处理模式示例
|
||||
|
||||
展示各种错误处理的最佳实践
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Optional, List, Dict, Any
|
||||
from functools import wraps
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# ============================================
|
||||
# 模式 1:具体异常捕获
|
||||
# ============================================
|
||||
|
||||
def read_file(filepath: str) -> Optional[str]:
|
||||
"""
|
||||
捕获具体异常而不是宽泛的 Exception
|
||||
"""
|
||||
try:
|
||||
with open(filepath, 'r') as f:
|
||||
return f.read()
|
||||
except FileNotFoundError:
|
||||
logger.error(f"文件不存在: {filepath}")
|
||||
return None
|
||||
except PermissionError:
|
||||
logger.error(f"没有权限读取文件: {filepath}")
|
||||
return None
|
||||
except UnicodeDecodeError:
|
||||
logger.error(f"文件编码错误: {filepath}")
|
||||
return None
|
||||
|
||||
|
||||
# ============================================
|
||||
# 模式 2:带重试的操作
|
||||
# ============================================
|
||||
|
||||
def retry_operation(max_attempts: int = 3):
|
||||
"""
|
||||
装饰器:自动重试失败的操作
|
||||
"""
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
for attempt in range(1, max_attempts + 1):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except Exception as e:
|
||||
if attempt == max_attempts:
|
||||
logger.error(f"操作失败,已重试 {max_attempts} 次: {e}")
|
||||
raise
|
||||
logger.warning(f"操作失败,第 {attempt} 次重试...")
|
||||
return None
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
@retry_operation(max_attempts=3)
|
||||
def unstable_api_call() -> Dict[str, Any]:
|
||||
"""
|
||||
模拟不稳定的 API 调用
|
||||
"""
|
||||
import random
|
||||
if random.random() < 0.7: # 70% 失败率
|
||||
raise ConnectionError("API 连接失败")
|
||||
return {"status": "success", "data": "result"}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 模式 3:上下文管理器处理资源
|
||||
# ============================================
|
||||
|
||||
class DatabaseConnection:
|
||||
"""
|
||||
自定义上下文管理器,确保资源正确释放
|
||||
"""
|
||||
def __init__(self, connection_string: str):
|
||||
self.connection_string = connection_string
|
||||
self.connection = None
|
||||
|
||||
def __enter__(self):
|
||||
logger.info(f"连接数据库: {self.connection_string}")
|
||||
self.connection = f"Connection to {self.connection_string}"
|
||||
return self.connection
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
if exc_type is not None:
|
||||
logger.error(f"发生异常: {exc_val}")
|
||||
logger.info("关闭数据库连接")
|
||||
# 清理资源
|
||||
self.connection = None
|
||||
return False # 不抑制异常
|
||||
|
||||
|
||||
# ============================================
|
||||
# 模式 4:链式异常(保留原始异常)
|
||||
# ============================================
|
||||
|
||||
def validate_and_process(data: Dict[str, Any]) -> Any:
|
||||
"""
|
||||
使用 raise from 保留原始异常链
|
||||
"""
|
||||
try:
|
||||
# 验证数据
|
||||
if 'value' not in data:
|
||||
raise ValueError("数据中缺少 'value' 字段")
|
||||
|
||||
value = data['value']
|
||||
if not isinstance(value, (int, float)):
|
||||
raise TypeError(f"value 应该是数字,实际类型: {type(value)}")
|
||||
|
||||
# 处理数据
|
||||
return value * 2
|
||||
|
||||
except (ValueError, TypeError) as e:
|
||||
# 保留原始异常并添加上下文
|
||||
raise RuntimeError(f"数据处理失败: {data}") from e
|
||||
|
||||
|
||||
# ============================================
|
||||
# 模式 5:结果对象模式(不使用异常)
|
||||
# ============================================
|
||||
|
||||
class Result:
|
||||
"""
|
||||
结果对象模式:封装成功/失败状态
|
||||
"""
|
||||
def __init__(self, success: bool, value: Any = None, error: str = None):
|
||||
self.success = success
|
||||
self.value = value
|
||||
self.error = error
|
||||
|
||||
@classmethod
|
||||
def ok(cls, value: Any) -> 'Result':
|
||||
return cls(success=True, value=value)
|
||||
|
||||
@classmethod
|
||||
def err(cls, error: str) -> 'Result':
|
||||
return cls(success=False, error=error)
|
||||
|
||||
def is_ok(self) -> bool:
|
||||
return self.success
|
||||
|
||||
def is_err(self) -> bool:
|
||||
return not self.success
|
||||
|
||||
def unwrap(self) -> Any:
|
||||
if not self.success:
|
||||
raise ValueError(f"尝试解包错误结果: {self.error}")
|
||||
return self.value
|
||||
|
||||
def unwrap_or(self, default: Any) -> Any:
|
||||
return self.value if self.success else default
|
||||
|
||||
|
||||
def safe_divide_result(a: float, b: float) -> Result:
|
||||
"""
|
||||
使用结果对象而不是异常
|
||||
"""
|
||||
if b == 0:
|
||||
return Result.err(f"除数不能为零: {b}")
|
||||
|
||||
try:
|
||||
return Result.ok(a / b)
|
||||
except Exception as e:
|
||||
return Result.err(f"计算失败: {e}")
|
||||
|
||||
|
||||
# ============================================
|
||||
# 模式 6:多项验证错误收集
|
||||
# ============================================
|
||||
|
||||
class ValidationError(Exception):
|
||||
"""自定义验证错误"""
|
||||
def __init__(self, errors: List[str]):
|
||||
self.errors = errors
|
||||
super().__init__("\n".join(errors))
|
||||
|
||||
|
||||
def validate_user(data: Dict[str, Any]) -> None:
|
||||
"""
|
||||
收集所有验证错误而不是遇到第一个就停止
|
||||
"""
|
||||
errors = []
|
||||
|
||||
if 'name' not in data:
|
||||
errors.append("缺少 'name' 字段")
|
||||
elif not isinstance(data['name'], str):
|
||||
errors.append("'name' 应该是字符串")
|
||||
elif len(data['name']) < 2:
|
||||
errors.append("'name' 长度应该至少为 2")
|
||||
|
||||
if 'age' not in data:
|
||||
errors.append("缺少 'age' 字段")
|
||||
elif not isinstance(data['age'], int):
|
||||
errors.append("'age' 应该是整数")
|
||||
elif data['age'] < 0 or data['age'] > 150:
|
||||
errors.append("'age' 应该在 0-150 之间")
|
||||
|
||||
if 'email' in data and '@' not in data['email']:
|
||||
errors.append("'email' 格式不正确")
|
||||
|
||||
if errors:
|
||||
raise ValidationError(errors)
|
||||
|
||||
|
||||
# ============================================
|
||||
# 模式 7:默认值和回退
|
||||
# ============================================
|
||||
|
||||
def get_config(config: Dict[str, Any], key: str, default: Any = None) -> Any:
|
||||
"""
|
||||
安全获取配置,支持多级键和默认值
|
||||
"""
|
||||
if '.' in key:
|
||||
# 支持嵌套键,如 "database.host"
|
||||
keys = key.split('.')
|
||||
value = config
|
||||
for k in keys:
|
||||
if isinstance(value, dict) and k in value:
|
||||
value = value[k]
|
||||
else:
|
||||
return default
|
||||
return value
|
||||
|
||||
# 简单键
|
||||
return config.get(key, default)
|
||||
|
||||
|
||||
# ============================================
|
||||
# 模式 8:优雅降级
|
||||
# ============================================
|
||||
|
||||
def get_user_preferences(user_id: int) -> Dict[str, Any]:
|
||||
"""
|
||||
尝试多种方法获取用户偏好,优雅降级
|
||||
"""
|
||||
# 尝试 1:从缓存获取
|
||||
try:
|
||||
return _get_from_cache(user_id)
|
||||
except Exception as e:
|
||||
logger.warning(f"从缓存获取失败: {e}")
|
||||
|
||||
# 尝试 2:从数据库获取
|
||||
try:
|
||||
return _get_from_database(user_id)
|
||||
except Exception as e:
|
||||
logger.warning(f"从数据库获取失败: {e}")
|
||||
|
||||
# 尝试 3:使用默认配置
|
||||
logger.info("使用默认配置")
|
||||
return _get_default_preferences()
|
||||
|
||||
|
||||
def _get_from_cache(user_id: int) -> Dict[str, Any]:
|
||||
# 模拟缓存失败
|
||||
raise ConnectionError("缓存连接失败")
|
||||
|
||||
|
||||
def _get_from_database(user_id: int) -> Dict[str, Any]:
|
||||
# 模拟数据库失败
|
||||
raise ConnectionError("数据库连接失败")
|
||||
|
||||
|
||||
def _get_default_preferences() -> Dict[str, Any]:
|
||||
return {
|
||||
"theme": "light",
|
||||
"language": "zh-CN",
|
||||
"notifications": True
|
||||
}
|
||||
|
||||
|
||||
# ============================================
|
||||
# 测试代码
|
||||
# ============================================
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
print("=" * 50)
|
||||
print("错误处理模式示例")
|
||||
print("=" * 50)
|
||||
|
||||
# 测试结果对象模式
|
||||
print("\n1. 结果对象模式:")
|
||||
result1 = safe_divide_result(10, 2)
|
||||
print(f"10 / 2 = {result1.unwrap()}")
|
||||
|
||||
result2 = safe_divide_result(10, 0)
|
||||
print(f"10 / 0 = {result2.unwrap_or('N/A')} ({result2.error})")
|
||||
|
||||
# 测试优雅降级
|
||||
print("\n2. 优雅降级:")
|
||||
prefs = get_user_preferences(123)
|
||||
print(f"用户偏好: {prefs}")
|
||||
|
||||
# 测试上下文管理器
|
||||
print("\n3. 上下文管理器:")
|
||||
try:
|
||||
with DatabaseConnection("localhost:5432") as conn:
|
||||
print(f"连接: {conn}")
|
||||
except Exception as e:
|
||||
print(f"操作失败: {e}")
|
||||
Reference in New Issue
Block a user