自学Python的讨论常集中在学习计划,但不少误区最终会落到可运行代码上:递归函数是否可用、异常是否可排查、容器是否选对、调试是否高效、优化是否过早。下面从原文给出的示例中抽取与脚本编程直接相关的部分,按“现象—原因—改法—适用场景”整理,代码保留原文逻辑并做必要说明。
1. 递归Fibonacci:先解决正确性和可维护性
原文的未缓存递归版本在n=40时就会非常慢,且没有类型提示、文档字符串,递归深度也有限制。原因是fibonacci(n)会重复计算大量子问题。改法是使用functools.lru_cache缓存结果,并补上参数校验和类型提示。maxsize=None表示不限制缓存条目;n从0开始,返回第n项。调用方式为fibonacci(10)。适用场景是教学演示和小规模递归;生产环境大n值仍应优先迭代实现,并关注缓存内存。- from functools import lru_cache
- @lru_cache(maxsize=None)
- def fibonacci(n: int) -> int:
- '''计算斐波那契数列第n项。n从0开始,返回第n项。'''
- if n < 0:
- raise ValueError('n must be non-negative')
- if n <= 1:
- return n
- 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字段→空值。适用场景:应用启动加载配置。- import json
- class ConfigError(Exception):
- pass
- def read_config(filename: str) -> str:
- '''读取配置文件并返回数据库主机地址。'''
- try:
- with open(filename, 'r', encoding='utf-8') as f:
- config = json.load(f)
- except FileNotFoundError:
- raise ConfigError(f'配置文件不存在: {filename}') from None
- except json.JSONDecodeError as e:
- raise ConfigError(f'配置文件JSON格式错误: {e}') from e
- except PermissionError:
- raise ConfigError(f'没有权限读取配置文件: {filename}') from None
- try:
- host = config['database']['host']
- except KeyError as e:
- raise ConfigError(f'配置文件中缺少必要字段: {e}') from e
- if not host:
- raise ConfigError('数据库主机地址不能为空')
- 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、滑动窗口。- from collections import Counter, defaultdict, deque
- import heapq
- data_set = set(range(1000000))
- if 999999 in data_set:
- print('找到了')
- text = 'the quick brown fox jumps over the lazy dog the fox'
- word_counts = Counter(text.lower().split())
- print(word_counts.most_common(3))
- items = [('fruit', 'apple'), ('veggie', 'carrot'), ('fruit', 'banana')]
- groups = defaultdict(list)
- for category, item in items:
- groups[category].append(item)
- print(dict(groups))
- numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
- largest_three = heapq.nlargest(3, numbers)
- print(largest_three)
- history = deque(maxlen=5)
- for page in ['home', 'products', 'product/1', 'cart', 'checkout', 'home']:
- history.append(page)
- print(list(history))
复制代码
4. 调试:从print过渡到logging和断点
原文指出只用print调试效率低:检查多个变量要反复加删print。logging可控制级别和格式,logger.info记录流程,logger.debug记录细节;VS Code中可在关键行设断点,F5启动调试,F10单步执行,F11进入函数,在左侧面板查看变量,在调试控制台执行表达式。排查流程:先看异常栈和日志级别,再在输入、循环、异常分支设断点。适用场景:脚本流程排查、订单处理等循环逻辑。- import logging
- logging.basicConfig(
- level=logging.DEBUG,
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
- )
- logger = logging.getLogger(__name__)
- def process_orders(orders):
- logger.info(f'开始处理 {len(orders)} 个订单')
- results = []
- for i, order in enumerate(orders):
- order_id = order['id']
- logger.debug(f'处理订单 #{i}: {order_id}')
- total = 0
- for item in order['items']:
- item_name = item['name']
- price = item['price'] * item['quantity']
- logger.debug(f' 商品 {item_name}: {price}')
- total += price
- logger.info(f'订单 #{order_id} 总金额: {total}')
- results.append(total)
- return results
复制代码
5. 过早优化:先能工作再优化
原文的单词统计示例中,mmap+流式处理的版本对初学者属于过度设计,简单版本用Counter逐行读取即可。原则是先让它能工作,再让它正确,最后在确认瓶颈后让它快。只有大文件I/O确实成为瓶颈时,才考虑mmap等方案。参数filename是文本文件路径,返回单词到次数的字典。适用场景:普通文本统计先用简单实现;性能优化必须有测量依据。- from collections import Counter
- from typing import Dict
- def count_words(filename: str) -> Dict[str, int]:
- '''统计文件中的单词出现次数。'''
- counter = Counter()
- with open(filename, 'r', encoding='utf-8') as f:
- for line in f:
- words = line.lower().split()
- counter.update(words)
- return dict(counter)
复制代码
技术自检可以看五点:递归函数是否考虑了重复计算和深度;异常是否捕获特定类型并记录原因;百万级成员检查是否误用list;调试是否只有print;未测量就上mmap等复杂方案。把这些点落到每天可运行的代码和提交记录里,比单纯收藏教程更能暴露问题。 |