
前言「快速入门」最大的陷阱是把「语法速查」当成「上手」。查一次sorted的签名不难难的是拿到一份陌生代码时知道从哪看起、写一个小工具时知道骨架怎么搭。这篇指南就按这两条线组织上半篇给你能直接套用的骨架下半篇给你一眼看懂别人代码的阅读顺序。提前说明版本问题Python 2.7 已于 2020 年 1 月 1 日停止维护。老资料里的print x、raw_input()、except X, e、d.iteritems()在 Python 3 里全部不能用。本文只写 Python 3示例适用于 3.8 及以上。另外纠正一个常见说法「Python 是解释型语言不需要编译」。CPython 是先把源码编译成字节码再由虚拟机执行的dis.dis()能看到指令。知道你写的东西会先变成字节码有助于理解为什么模块会被缓存成__pycache__里的.pyc。一、五分钟核心语法速查主题语法关键提醒赋值a 1a, b 1, 2是名字绑定不是复制输出print(x, sep..., end...)print是函数不是语句输入input(提示)返回str务必转换条件if / elif / else冒号 缩进无switch循环for x in iterable:/while cond:range左闭右开函数def f(a, b1, *args, **kw):默认参数只求值一次类class C:/def __init__(self):首参是self异常try / except X as e / finally不写裸except:导入import x/from x import yimport会执行顶层代码推导式[f(x) for x in xs if p(x)]有自己的作用域二、五套实战骨架可直接运行骨架一脚本最小可直接运行的脚本长这样# 适用于 Python 3.8一句话说明这个脚本做什么。import sysdef main(argv):if len(argv) ! 2:print(f用法python {argv[0]} 目标参数, filesys.stderr)return 2print(收到, argv[1])return 0if __name__ __main__:raise SystemExit(main(sys.argv))三个约定main()接收参数列表并返回退出码错误信息写到sys.stderr而不是stdoutraise SystemExit(main(...))把返回值当退出码。sys.argv[0]是脚本自身的名字。骨架二命令行工具参数超过两个就别手撸sys.argv了# 适用于 Python 3.8import argparsedef build_parser():parser argparse.ArgumentParser(description演示用命令行工具)parser.add_argument(src, help输入路径)parser.add_argument(-o, --output, default-, help输出路径默认标准输出)parser.add_argument(-n, --count, typeint, default10, help处理条数)parser.add_argument(-v, --verbose, actionstore_true, help输出详细日志)return parserif __name__ __main__:args build_parser().parse_args()print(args.src, args.output, args.count, args.verbose)位置参数不带前缀可选参数带-/--typeint负责转换和报错actionstore_true把开关变成布尔值。argparse还会自动生成-h帮助。骨架三文件处理# 适用于 Python 3.8from pathlib import Pathdef process(src: Path, dst: Path) - int:逐行处理返回处理的行数。count 0with src.open(encodingutf-8) as fin, dst.open(w, encodingutf-8) as fout:for line in fin:stripped line.rstrip(\n)if not stripped:continuefout.write(stripped.upper() \n)count 1return countif __name__ __main__:print(process(Path(in.txt), Path(out.txt)))要点with支持一行开两个文件3.10 起还能写成带括号的多行形式encoding必须显式写大文件用for line in fin:惰性读不要readlines()。骨架四异常与日志# 适用于 Python 3.8import loggingimport syslogging.basicConfig(levellogging.INFO,format%(asctime)s %(levelname)s %(name)s %(message)s,streamsys.stderr,)logger logging.getLogger(__name__)def divide(a, b):if b 0:raise ValueError(除数不能为零)return a / btry:result divide(1, 0)except ValueError as exc:logger.warning(计算失败%s, exc)else:print(result)finally:logger.info(结束)用logger.warning(模板 %s, 值)而不是 f-string 拼好再传这样日志级别被过滤时不会白白做字符串格式化。异常只捕获你真正能处理的那一层捕获不了就让它往上抛。骨架五正则与 JSON# 适用于 Python 3.8import jsonimport reKV re.compile(r(\w)(\d))text set width20 and height10print(KV.findall(text)) # [(width, 20), (height, 10)]print(re.sub(r\d, #, text)) # set width# and height#print(re.search(r\d, text).group())payload json.dumps({名字: 小明}, ensure_asciiFalse, indent2)print(payload)print(json.loads(payload)[名字])re.compile后复用模式对象比每次调用re.findall传字符串更清晰。re.findall的签名是re.findall(pattern, string, flags0)有捕获组时返回元组列表re.sub是re.sub(pattern, repl, string, count0, flags0)。json.dumps的ensure_ascii默认是True中文会变成\uXXXX。三、几个必须先分清的点re.match与re.search不一样函数从哪里开始匹配是否需要整串匹配re.match只在字符串开头否只要求开头匹配re.search任意位置否re.fullmatch整串是# 适用于 Python 3.8import reprint(bool(re.match(r\d, abc123))) # False开头不是数字print(bool(re.search(r\d, abc123))) # Trueprint(bool(re.fullmatch(r\d, 123))) # Trueprint(bool(re.fullmatch(r\d, 123abc))) # False用re.match(pattern, s)却期望「整串是数字」是高频错误正确工具是re.fullmatch。另外不要用正则解析 HTML 或 JSON——那是另一类工具的事。四、八步读懂一份陌生的 Python 代码拿到别人写的脚本按这个顺序看比从第一行读起快得多看有没有if __name__ __main__:找到真正的入口。看 import标准库 / 第三方 / 本地三组能大致判断它在干什么。看顶层常量与配置大写名字通常是常量。看def的签名参数名和默认值比函数体更能说明接口。看类型标注如果有def f(x: int) - str:直接告诉你不该传什么。看class的__init__实例属性全在那里。看异常处理except捕获什么说明作者预见什么失败。最后才看循环与分支的细节。# 适用于 Python 3.8from dataclasses import dataclassdataclassclass Job:name: strretries: int 3tags: tuple ()def label(self) - str:return f{self.name}({self.retries})print(Job(backup, tags(nightly,)))类型标注和dataclasses是阅读代码时最省力的线索dataclass会自动生成__init__、__repr__、__eq__所以类体里看不到构造逻辑是正常的不必到处找。常见坑点❌ 用re.match(r\d, s)判断「整个字符串是数字」 ✅re.match只要求开头匹配123abc也会通过要整串匹配用re.fullmatch。❌ 以为input()返回数字 ✅ 它永远返回strinput() 1会抛TypeError先转换并处理ValueError。❌ 用logging.warning(f值 {v})图省事 ✅ 写logging.warning(值 %s, v)让格式化在级别过滤之后才发生。❌ 在脚本里直接用sys.argv[1]✅ 参数缺失会抛IndexError用argparse或先检查len(sys.argv)。❌ 打开文件不写encoding✅ 默认编码是平台相关的跨平台必须写encodingutf-8。❌ 照着老资料写except ValueError, e:✅ 这是 Python 2 语法早已移除Python 3 写except ValueError as e:。❌ 用裸except:让脚本「不崩」 ✅ 它会吞掉KeyboardInterrupt和SystemExit脚本会变成 CtrlC 都杀不掉的僵尸至少写except Exception:。❌ 认为import不会执行任何代码 ✅import会执行模块顶层语句有副作用的代码要放进main()并用if __name__ __main__:保护。总结需求直接套用的骨架关键点写脚本main(argv)raise SystemExit(...)返回退出码错误走 stderr收参数argparse.ArgumentParsertype负责转换与报错处理文件with src.open(encodingutf-8) as fin:显式编码惰性逐行打日志logging.getLogger(__name__)用%s占位不用 f-string匹配文本re.compilefullmatch/search分清 match 与 fullmatch序列化json.dumps(..., ensure_asciiFalse)中文要关掉 ASCII 转义读代码入口 → import → 签名 → 类 → 异常别从第一行硬读快速入门的正确姿势不是把语法表背下来而是记住几个能改就用的骨架再带着「名字绑定不是复制」「input()返回字符串」「re.match不要求整串匹配」这三类高频陷阱去读别人的代码。骨架省下的是敲键盘的时间陷阱省下的是调试的时间。