page contents

Python脚本别硬写:10个高频技巧,让代码更短更稳

写 Python 脚本,麻烦常来自顺手写法。字符串拼路径。循环里判断 key 在不在。读整个文件再过滤。手写类只装两三个字段。代码能跑。改动时费劲。换一种写法。代码行数少。数据流直。下面这些技巧来自日常脚本。不追求语法花样。只解决重复动作。

attachments-2026-10-2TebnZNp6ae5500629c9f.png

写 Python 脚本,麻烦常来自顺手写法。字符串拼路径。循环里判断 key 在不在。读整个文件再过滤。手写类只装两三个字段。代码能跑。改动时费劲。换一种写法。代码行数少。数据流直。下面这些技巧来自日常脚本。不追求语法花样。只解决重复动作。

路径处理先用 pathlib。字符串拼目录分隔符,Windows 和 Linux 表现不同。Path 对象用 / 拼路径。rglob 找文件。看这个统计日志行数的函数。

from pathlib import Path  def count_lines(root: str) -> int:     total = 0     for path in Path(root).rglob('*.log'):         with path.open(encoding='utf-8') as f:             total += sum(1 for _ in f)     return total

rglob 返回生成器。文件一个一个遍历。sum(1 for _ in f) 不建行列表。大文件也只占一行内存。编码写明白。读文件出错就让调用方看到。函数不藏异常。

大文件逐行筛选用生成器。readlines 会把整个文件拉进列表。文件一大,内存就顶上去。写一个只产筛选行的函数。

def pick(lines, word):     for line in lines:         if word in line:             yield line.rstrip('\n')

调用时把文件对象传进去。pick 不知道文件从哪来。它只负责筛选。rstrip 只去行尾换行。多个筛选条件可以接多个生成器。每一层只做一件事。管道式写法读起来顺。

计数别写 if key not in d。defaultdict 能省一层判断。按状态码统计。

from collections import defaultdict  def count_status(lines):     stats = defaultdict(int)     for line in lines:         if not line.strip():             continue         code = line.split()[0]         stats[code] += 1     return dict(stats)

defaultdict(int) 遇到新 key 先给 0。+= 直接累加。返回普通 dict。调用方拿到结果后不会误加新 key。空行先跳过。split()[0] 前要保证行里有内容。这个检查放在循环里。边界写清楚。

只装字段的类用 dataclass。手写 __init__、__repr__、__eq__ 容易漏。配置对象用 frozen。字段不改。slots 限制属性。内存也少一点。

from dataclasses import dataclass  @dataclass(frozen=True, slots=True) class Result:     name: str     count: int     ok: bool

frozen 让对象构造后只读。slots 需要 Python 3.10 以上。字段类型写出来。编辑器能提示。值对象适合这样写。带行为的类另说。别为了省事把所有类都套 dataclass。

已知异常用 suppress 包住。删临时文件常遇到文件不存在。try/except/pass 占四行。suppress 一行。

from contextlib import suppress from pathlib import Path  with suppress(FileNotFoundError):     Path('tmp.txt').unlink()

suppress 只包住预期异常。范围要小。别把大段逻辑塞进去。异常被吞掉,排查会难。这里只处理删文件。文件不存在就继续。其他错误照常抛出。

两个列表一起遍历,长度不一致会悄悄少数据。zip(strict=True) 直接报错。Python 3.10 起可用。

names = ['a', 'b'] scores = [90, 80] for name, score in zip(names, scores, strict=True):     print(name, score)

长度不同抛 ValueError。错误早出现。别用下标手写循环。zip 读起来直。strict 参数把隐患变成显式错误。数据对齐的场景适合。不需要对齐就别加 strict。

按类型分派用 singledispatch。一长串 isinstance 会越写越长。加新类型要改主函数。注册函数分开写。

from functools import singledispatch  @singledispatch def show(value):     return str(value)  @show.register def _(value: list):     return ', '.join(map(str, value))

默认实现处理常见值。list 单独注册。新类型再加一个注册函数。主函数不动。注册函数用下划线命名。类型注解决定分派。运行时分派看实际类型。子类也会匹配。需要精确匹配时另写判断。

取前几名不要全排序。heapq.nlargest 只维护 n 个候选。数据多时省时间。

import heapq  def top_words(counter, n=3):     return heapq.nlargest(n, counter.items(), key=lambda item: item[1])

counter 可以是 dict。items 给键值对。key 取第二项。返回列表。n 小效果好。n 接近数据量时排序更直接。看数据规模选。别背技巧。看场景。

有序列表插入用 bisect.insort。每次 append 再 sort 会重复排序。insort 找到位置插进去。列表保持有序。

from bisect import insort  nums = [1, 3, 5] insort(nums, 4) print(nums)

插入成本在搬元素。列表小、插入频繁时好用。查找用 bisect_left。数据量大、插入多,考虑别的结构。工具都有边界。选之前看操作比例。

命令解析用 argparse 子命令绑函数。if args.cmd == 'build' 会越堆越多。set_defaults 把函数挂到参数上。

import argparse  def build(args):     print('build', args.name)  def clean(args):     print('clean', args.name)  parser = argparse.ArgumentParser() sub = parser.add_subparsers(required=True)  p = sub.add_parser('build') p.add_argument('name') p.set_defaults(func=build)  p = sub.add_parser('clean') p.add_argument('name') p.set_defaults(func=clean)  args = parser.parse_args() args.func(args)

每个子命令一个函数。主流程只剩 args.func(args)。加命令不改分发代码。required=True 让空命令直接报错。参数校验交给 argparse。函数只处理业务。

这些写法有一个共同点。让数据流短。让边界显式。让异常只出现在该出现的地方。Python 的语法够用。关键是别把临时写法留成长期结构。挑一个今天能用的技巧。放进手头脚本。跑一遍。看行数和改动点。你会知道值不值。

更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

 

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

2541 篇文章

作家榜 »

  1. Pack 2541 文章
  2. 轩辕小不懂 2403 文章
  3. 小柒 2228 文章
  4. Nen 576 文章
  5. 王昭君 216 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章