查看: 143|回复: 3

Python递归缓存、异常处理与容器选型错误排查

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
自学Python的讨论常集中在学习计划,但不少误区最终会落到可运行代码上:递归函数是否可用、异常是否可排查、容器是否选对、调试是否高效、优化是否过早。下面从原文给出的示例中抽取与脚本编程直接相关的部分,按“现象—原因—改法—适用场景”整理,代码保留原文逻辑并做必要说明。

1. 递归Fibonacci:先解决正确性和可维护性
原文的未缓存递归版本在n=40时就会非常慢,且没有类型提示、文档字符串,递归深度也有限制。原因是fibonacci(n)会重复计算大量子问题。改法是使用functools.lru_cache缓存结果,并补上参数校验和类型提示。maxsize=None表示不限制缓存条目;n从0开始,返回第n项。调用方式为fibonacci(10)。适用场景是教学演示和小规模递归;生产环境大n值仍应优先迭代实现,并关注缓存内存。
  1. from functools import lru_cache
  2. @lru_cache(maxsize=None)
  3. def fibonacci(n: int) -> int:
  4.     '''计算斐波那契数列第n项。n从0开始,返回第n项。'''
  5.     if n < 0:
  6.         raise ValueError('n must be non-negative')
  7.     if n <= 1:
  8.         return n
  9.     return fibonacci(n - 1) + fibonacci(n - 2)
复制代码

2. 配置读取:捕获特定异常并保留原因
原文的read_config直接open并json.load,再取config['database']['host'],可能触发FileNotFoundError、json.JSONDecodeError、KeyError、PermissionError,文件编码不对时还可能UnicodeDecodeError。正确做法是分层捕获:文件不存在、JSON格式错误、权限不足分别转换为自定义ConfigError;字段缺失也转为ConfigError;host为空则拒绝。from None/from e保留或切断异常链,便于排查。参数filename是配置文件路径,返回数据库主机地址。排查顺序:路径和权限→文件编码→JSON格式→database/host字段→空值。适用场景:应用启动加载配置。
  1. import json
  2. class ConfigError(Exception):
  3.     pass
  4. def read_config(filename: str) -> str:
  5.     '''读取配置文件并返回数据库主机地址。'''
  6.     try:
  7.         with open(filename, 'r', encoding='utf-8') as f:
  8.             config = json.load(f)
  9.     except FileNotFoundError:
  10.         raise ConfigError(f'配置文件不存在: {filename}') from None
  11.     except json.JSONDecodeError as e:
  12.         raise ConfigError(f'配置文件JSON格式错误: {e}') from e
  13.     except PermissionError:
  14.         raise ConfigError(f'没有权限读取配置文件: {filename}') from None
  15.     try:
  16.         host = config['database']['host']
  17.     except KeyError as e:
  18.         raise ConfigError(f'配置文件中缺少必要字段: {e}') from e
  19.     if not host:
  20.         raise ConfigError('数据库主机地址不能为空')
  21.     return host
复制代码

3. 容器选型:复杂度决定写法
原文用100万元素做成员检查,list的in是O(n),set的in平均O(1),原文示例给出性能差异巨大。Counter适合词频统计,most_common(3)取前3;defaultdict(list)适合分组,访问不存在的键会自动创建列表;heapq.nlargest(3, numbers)取Top K;deque(maxlen=5)保留最近5条历史。参数含义:maxlen限制队列长度,nlargest的第一个参数是数量。适用场景:去重和成员检查、计数、分组聚合、Top K、滑动窗口。
  1. from collections import Counter, defaultdict, deque
  2. import heapq
  3. data_set = set(range(1000000))
  4. if 999999 in data_set:
  5.     print('找到了')
  6. text = 'the quick brown fox jumps over the lazy dog the fox'
  7. word_counts = Counter(text.lower().split())
  8. print(word_counts.most_common(3))
  9. items = [('fruit', 'apple'), ('veggie', 'carrot'), ('fruit', 'banana')]
  10. groups = defaultdict(list)
  11. for category, item in items:
  12.     groups[category].append(item)
  13. print(dict(groups))
  14. numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
  15. largest_three = heapq.nlargest(3, numbers)
  16. print(largest_three)
  17. history = deque(maxlen=5)
  18. for page in ['home', 'products', 'product/1', 'cart', 'checkout', 'home']:
  19.     history.append(page)
  20. print(list(history))
复制代码

4. 调试:从print过渡到logging和断点
原文指出只用print调试效率低:检查多个变量要反复加删print。logging可控制级别和格式,logger.info记录流程,logger.debug记录细节;VS Code中可在关键行设断点,F5启动调试,F10单步执行,F11进入函数,在左侧面板查看变量,在调试控制台执行表达式。排查流程:先看异常栈和日志级别,再在输入、循环、异常分支设断点。适用场景:脚本流程排查、订单处理等循环逻辑。
  1. import logging
  2. logging.basicConfig(
  3.     level=logging.DEBUG,
  4.     format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
  5. )
  6. logger = logging.getLogger(__name__)
  7. def process_orders(orders):
  8.     logger.info(f'开始处理 {len(orders)} 个订单')
  9.     results = []
  10.     for i, order in enumerate(orders):
  11.         order_id = order['id']
  12.         logger.debug(f'处理订单 #{i}: {order_id}')
  13.         total = 0
  14.         for item in order['items']:
  15.             item_name = item['name']
  16.             price = item['price'] * item['quantity']
  17.             logger.debug(f' 商品 {item_name}: {price}')
  18.             total += price
  19.         logger.info(f'订单 #{order_id} 总金额: {total}')
  20.         results.append(total)
  21.     return results
复制代码

5. 过早优化:先能工作再优化
原文的单词统计示例中,mmap+流式处理的版本对初学者属于过度设计,简单版本用Counter逐行读取即可。原则是先让它能工作,再让它正确,最后在确认瓶颈后让它快。只有大文件I/O确实成为瓶颈时,才考虑mmap等方案。参数filename是文本文件路径,返回单词到次数的字典。适用场景:普通文本统计先用简单实现;性能优化必须有测量依据。
  1. from collections import Counter
  2. from typing import Dict
  3. def count_words(filename: str) -> Dict[str, int]:
  4.     '''统计文件中的单词出现次数。'''
  5.     counter = Counter()
  6.     with open(filename, 'r', encoding='utf-8') as f:
  7.         for line in f:
  8.             words = line.lower().split()
  9.             counter.update(words)
  10.     return dict(counter)
复制代码

技术自检可以看五点:递归函数是否考虑了重复计算和深度;异常是否捕获特定类型并记录原因;百万级成员检查是否误用list;调试是否只有print;未测量就上mmap等复杂方案。把这些点落到每天可运行的代码和提交记录里,比单纯收藏教程更能暴露问题。
回复

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: Python递归缓存、异常处理与容器选型错误排查

这篇整理很实用,尤其是按“现象—原因—改法—适用场景”展开,比只列知识点更容易落到可运行代码上。递归缓存那部分把正确性和可维护性放在性能前面很赞同,生产环境大 n 确实还要考虑迭代实现和缓存内存;配置读取分层捕获异常、保留异常链的做法也很值得照着排查。容器选型里用 set 做成员检查、Counter 计数、defaultdict 分组、heapq 取 Top K、deque 做滑动窗口,场景对应得很清楚。调试部分从 print 过渡到 logging 和断点,也符合脚本排查的实际流程。 另外提醒一下:第 3 节分组示例里 `groups.append(item)` 看起来像是笔误,按上下文应该是按 category 往对应列表里追加;第 4 节末尾 logging 代码似乎被截断了,发帖时可以补完整,方便读者直接跑。整体内容对自学 Python 的脚本编程很有帮助。
回复 支持 反对

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: Python递归缓存、异常处理与容器选型错误排查

楼主这种按“现象—原因—改法—适用场景”整理的方式很清晰,特别适合把自学中的误区落到可运行代码上。递归 Fibonacci 用 lru_cache 解决重复计算很直接,同时也提醒生产环境大 n 优先迭代、关注缓存增长,这点很实用。配置读取部分分层捕获异常并保留异常链,排查顺序也交代得明白,比笼统 try-except 更容易定位问题。容器选型里 set、Counter、defaultdict、heapq、deque 的适用场景讲得比较直观;不过分组那段代码里 `groups.append(item)` 应该写成 `groups.append(item)`,否则 defaultdict(list) 没有 append 方法,运行会报错。调试部分从 print 过渡到 logging 和断点也很实际,建议日志里带上异常栈和关键上下文,断点优先设在输入、循环边界和异常分支。整体内容对脚本编程排查很有参考价值。
回复 支持 反对

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: Python递归缓存、异常处理与容器选型错误排查

这帖整理得挺实用的,尤其把每个点按“现象—原因—改法—适用场景”串起来,排查时容易对照。递归那部分补上 lru_cache、参数校验和文档字符串后,正确性和可读性都好很多;同时提醒生产环境大 n 优先迭代这一点也很关键。配置读取的分层捕获和 from e/from None 保留异常链,对定位路径、编码、JSON、字段问题很有帮助。容器选型那段把 list/set/Counter/defaultdict/heapq/deque 的场景讲清楚了。 不过分组示例里有一处小笔误:groups = defaultdict(list) 后循环里应该用 groups.append(item),现在写的 groups.append(item) 会报 AttributeError,补上后示例就更完整了。第4部分 logging 好像没贴完,期待后续把断点和日志级别排查也补全。整体对自学 Python 落到可运行代码很有参考价值。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-11-14 04:44 , Processed in 0.022039 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部