简介:一份包含96个Python实例脚本的经典代码汇总,主要面向Python初学者与需要巩固基础的进阶开发者,覆盖基础语法、字符串与列表等数据类型操作、条件循环、函数与模块、类与对象、文件读写、异常处理、数据库连接、网络请求及数据可视化等核心开发场景。压缩包总大小仅53KB,全部为.py格式源码文件,轻量易用,可快速下载并导入本地编辑器运行。实例按JCP编号逐一排列,每个脚本对应一个独立知识点或常见任务,代码结构清晰、注释直白,便于初学者模仿学习,也方便有经验的开发者按需检索复用。目前已有770人学习下载,验证了其实用价值。通过运行和改写这些实例,学习者能够从简单语法练习平滑过渡到面向对象、数据库等进阶主题,既适合作为教学辅助材料,也可当作日常编程的速查手册,帮助切实提升编码效率与问题解决能力。
1. 一百个经典实例,真正被反复翻看的往往只有二十多个
如果你去搜“Python经典实例代码汇总+100例”,大概率会看到一份很长的清单:从读取CSV、计算斐波那契数列,到爬取网页、调用API,再到画折线图、写一个小游戏。可你拿到手之后会发现,能当场看懂并且愿意抄的,通常只有二十几个。剩下的大部分例子要么依赖版本太老的库,要么环境配置比代码本身还长,要么就是功能重复——同样一个排序,一口气给了五种写法。
经典实例的真正价值不是“代码数量”,而是让你用最短时间把常见需求映射到正确写法。你想把一堆文件批量改名,搜到os.rename;你拿到一个带脏数据的Excel,第一反应是pandas还是openpyxl;你想把脚本参数化,又不想装argparse外的额外依赖——这些单点答案才是“经典”两个字值钱的地方。这份汇总的用处就一个:给你一份可以反复翻查的起点,而不是让你从头到尾背完一百遍。适合两类人:刚学完基础语法、不知道下一步写什么的初学者;以及写了几个月脚本、遇到“这功能我记得见过但忘在哪”的熟手。下面我按“先立住选例标准,再讲怎么跑,最后讲怎么改”的思路,把这套东西讲透。
2. 经典实例的取舍逻辑:不是凑满一百个,而是每个都能在一个文件里跑完
2.1 按功能域拆成六类,覆盖日常脚本的绝大多数场景
一份能用的实例集合,一定不是按难度线性排列的,而是按功能域分组的。我常见到有人把“打印九九乘法表”和“多线程下载”放在同一个难度层级里,这没有意义。我习惯把经典实例拆成六类:文件与路径操作、文本与正则、数据清洗与转换、网络请求与解析、对象建模、以及小工具链(命令行参数、日志输出、断言验证)。六类覆盖公司里八成的脚本需求:处理Excel报表、批量重命名、定时检查接口、清洗爬回来的数据、把JSON转成CSV。
分类决定了你怎么查。比如你遇到的问题是“一个目录下几千个文件里挑出上周修改过的”,你应该去“文件与路径”那一组,而不是去“网络请求”那一组。多数经典实例汇总最大的问题不是没写代码,而是没写“这个问题属于哪个域”。当你学会先按问题域去找例子的时候,你的查代码速度会明显提升,这也是所谓“经验”的一部分。
2.2 选择标准只看三件事:依赖、运行成本、是否标准库能解决
我一般接一个需求,会先问自己一个问题:这个例子如果还要装一个第三方库,那这个库值不值得装?比如要把一段HTML里的链接提取出来,用正则三步能写完,就没必要上BeautifulSoup。反之,如果拿到的是“一个Excel里从第3行开始读取并且公式要保留”,那纯openpyxl是绕不过去的。
我挑实例的原则有三个。第一,优先标准库;第二,第三方库选生态最稳的,而不是最酷的;第三,每个例子必须单独能跑通,不依赖“先运行前面第20个例子”。因为一套经典实例的失败率最高的时刻,就是用户从第1个顺序跑到第50个,结果发现第50个例子用到了第49个例子里定义的全局变量。这不能怪用户,是例子组织本身的问题。一个独立文件、一个直接入口、一段“运行后能看到什么”的说明,这三件套比代码本身的语法水平更重要。
2.3 单文件脚本为主,模板式组织比工程化框架更适合“汇总”
经典实例没有必要用工程化目录组织:不需要src包、不需要setup.py、不需要用pytest来管理。因为读者的痛点是“我要快速拿一段代码去验证一个想法”,不是“我要参与一个大型项目”。所以我推荐的目录结构是扁平化的:
py_examples/ 01_file_path_rename.py 02_csv_merge_by_key.py 03_json_clean_nested.py 04_regex_extract_url.py 05_schedule_check_http.py文件名里带上三个信息:序号、功能域、动词。这样即使你只记得“当时有个例子读CSV的”,你搜csv就能搜到。这个好处在第一周感觉不明显,三个月后你往回翻的时候会感谢这个命名习惯。
到这里你大概明白了:经典实例的“汇总”本质上是在做一次需求维度的索引,而不是代码的堆叠。接下来要解决的是更实际的问题——我拿到这套代码,从哪里开始跑?下一章我用最小依赖的方式带你跑通前一批脚本。
3. 先把20个标准库例子跑通:最小依赖起步,再引入第三方库
3.1 环境准备:一个Python版本加一个requirements,够了
开始动手前,先说环境和依赖。不建议为了跑100个例子专门装Anaconda。一个干净的Python环境加上按需安装第三方库就够了。我用虚拟环境管理,避免“为了跑一个requests把整个环境搞乱”的情况。
mkdir py_examples && cd py_examples python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate python -m pip --version逻辑说明:python -m venv venv会在当前目录建一个隔离的Python环境,source venv/bin/activate切换进去。这样后面pip装的包只影响这个目录。这一步是给后面大量依赖第三方库的例子做隔离。参数说明:如果你的机器上Python版本比较多,可以用python3 -m venv venv指定具体版本;Windows PowerShell不用source,直接执行venv\Scripts\Activate.ps1。
接下来跑第一批纯标准库例子。这一批一共20个,全部不依赖外部包,适合用来做环境自检。我挑三个有代表性的展开说。
3.2 例子:统计目录下所有文件的大小与数量
# 01_dir_stats.py from pathlib import Path from collections import defaultdict def scan_directory(root: str) -> dict: """统计一个目录下各扩展名的文件数量与总大小""" stats = defaultdict(lambda: {"count": 0, "bytes": 0}) base = Path(root) for file in base.rglob("*"): if file.is_file(): ext = file.suffix.lower() or "no_ext" stats[ext]["count"] += 1 stats[ext]["bytes"] += file.stat().st_size return dict(stats) if __name__ == "__main__": import sys target = sys.argv[1] if len(sys.argv) > 1 else "." result = scan_directory(target) for ext, info in sorted(result.items(), key=lambda x: -x[1]["bytes"]): print(f"{ext:8s} {info['count']:5d} files {info['bytes'] / 1024:.1f} KB")逻辑说明:Path.rglob递归列出目录下所有文件,suffix取扩展名,file.stat().st_size获取字节数。用defaultdict避免判断键是否存在的样板代码。这段代码解决的核心问题:你拿到一个陌生目录,想快速知道“这里大概有什么类型的文件、每个类型占多少空间”。
参数说明:sys.argv[1]是命令行传入的目录路径,不给参数时默认扫描当前目录。sorted(..., key=lambda x: -x[1]["bytes"])按大小倒序输出,这样最大的文件类型排在最上面。如果你只想统计一层目录,不递归子目录,把rglob改成glob即可。
3.3 例子:合并两个CSV,按公共字段对齐
# 02_csv_merge_by_key.py import csv from collections import OrderedDict def merge_csv(file_a: str, file_b: str, key: str, output: str) -> None: """按指定键合并两个CSV,结果写入新文件""" data = OrderedDict() with open(file_a, "r", encoding="utf-8", newline="") as f: reader = csv.DictReader(f) for row in reader: data[row[key]] = row with open(file_b, "r", encoding="utf-8", newline="") as f: reader = csv.DictReader(f) for row in reader: uid = row[key] if uid in data: data[uid].update(row) else: data[uid] = row with open(output, "w", encoding="utf-8", newline="") as f: # 取所有字段的并集,保证不丢列 fieldnames = [] for row in data.values(): for k in row: if k not in fieldnames: fieldnames.append(k) writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerows(data.values()) if __name__ == "__main__": merge_csv("2024_sales.csv", "2025_target.csv", "sku_id", "merged.csv")逻辑说明:两个CSV都有sku_id字段,一个存销量,一个存年度目标。DictReader把每一行读成字典,data[uid].update(row)让右边文件的字段覆盖左边同键行的同名字段,但保留左边独有字段。最后写出的CSV是两表字段的并集。
参数说明:encoding="utf-8"处理中文内容,newline=""防止在Windows平台写出多余空行。key参数决定了合并是按“sku_id”还是其他字段对齐。如果两个CSV里键不唯一,这段代码会互相覆盖,此时应该改用list存储分组。
3.4 例子:运行时可选的第三方依赖,让脚本同时兼容两种情况
有些例子本身很简单,但依赖第三方库,这会让初学者翻车。常见做法是运行时检测库是否存在,存在就用库,不存在就走标准库路径。我常用的一段模板:
# 03_try_load_lib.py try: import pandas as pd HAS_PANDAS = True except ImportError: HAS_PANDAS = False def load_data(path: str): if HAS_PANDAS: return pd.read_csv(path) # 降级到标准库 import csv with open(path, "r", encoding="utf-8") as f: return list(csv.DictReader(f)) if __name__ == "__main__": rows = load_data("example.csv") print(f"读到 {len(rows)} 行,第一行: {rows[0] if rows else '空'}")逻辑说明:try...except ImportError检测pandas是否存在,存在则优先使用,否则用csv模块兜底。这让同一个脚本在干净环境和装了pandas的环境里都能运行。
参数说明:这里没有硬性参数,关键是HAS_PANDAS这个布尔值在程序里只读取一次,不要每次调用load_data都重新import检测。如果你希望强制使用纯标准库模式,可以把HAS_PANDAS改成从环境变量读:os.getenv("USE_PURE_PY", "0") == "1"。
跑完第一批20个标准库例子,你应该已经理解:经典实例里的代码本质是“解决方案片段”,而不是“完整项目”。接下来我拆解几个高频实例的改造思路,让你把“抄来的代码”改成“自己的工具”。
4. 高频实例拆解:从数据清洗到接口验证,改造成自己的写法
4.1 数据清洗三件套:类型转换、去重、补缺失
数据清洗是最常出现的实例类型。三个经典需求:字符串里的数字变成能计算的数字、重复行去掉、缺失字段补默认值。常见的写法是链式调用:
# 04_clean_data.py import re from decimal import Decimal def to_number(value) -> float: """把'12,345.67元'、'¥1,200'这类字符串转成float""" if isinstance(value, (int, float)): return float(value) s = str(value).replace(",", "").replace("元", "").replace("¥", "").strip() return float(s) if re.fullmatch(r"-?\d+(\.\d+)?", s) else 0.0 def deduplicate(rows, keys): """按keys列去重,保留第一次出现的行""" seen = set() result = [] for row in rows: marker = tuple(row.get(k) for k in keys) if marker not in seen: seen.add(marker) result.append(row) return result if __name__ == "__main__": sample = [ {"id": "A01", "price": "1,299.00元", "qty": "3"}, {"id": "A01", "price": "1,299.00元", "qty": "3"}, {"id": "A02", "price": "格式异常", "qty": "x"}, ] cleaned = deduplicate(sample, ["id"]) for row in cleaned: print(row)逻辑说明:to_number用正则做白名单校验,格式不合法时返回0.0,避免float("abc")直接抛异常中断整个任务。deduplicate用set记录已见组合,达到按任意多个键去重的效果。这两段几乎可以原样搬到任何清洗项目里。
参数说明:to_number里re.fullmatch要求整个字符串匹配数字格式,避免“123abc”被强行阶段性转换。如果你需要保留千分位“12,345.67”的小数,正则允许且有两位小数;如果数据里有负数,-?前缀已覆盖。deduplicate的keys参数传入列表,比如["id"]只按id去重,["id", "date"]按两列联合去重。
4.2 文本提取的经典正则:日志与HTML里的信息抓取
提取日志中的时间戳、错误码,或从HTML里取链接,方法论一致:先定位包围结构,再取结果。日志提取的写法:
# 05_extract_log.py import re LOG_PATTERN = re.compile( r"\[(?P<level>INFO|WARN|ERROR)\]\s+" r"(?P<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\s+" r"--\s+(?P<message>.*)" ) def parse_log_line(line: str): m = LOG_PATTERN.search(line) if not m: return None return m.groupdict() # {"level": "...", "timestamp": "...", "message": "..."} if __name__ == "__main__": test_line = "[ERROR] 2025-06-01 10:24:33 -- connect timeout after 3s" print(parse_log_line(test_line))逻辑说明:命名分组(?P<level>...)让结果直接变成字典,后续字段取值不用数索引,可读性高很多。search方法定位第一个匹配点,不用在意行首是否有空格。这个模式的实用场景是:给你一个几万行的日志文件,你只把ERROR和WARN筛出来,再做统计。
参数说明:re.compile在模块加载时只编译一次,而不是每行都重新编译,这在处理大文件时性能差异明显。\d{4}-\d{2}-\d{2}固定为年月日,如果你的日志带毫秒或时区,需要扩展\s+\+\d{4}。当多个模式的优先级不同时,用|连接要小心顺序,re默认从左到右匹配,长的模式要放前面。
4.3 文件批处理:批量改名与转码
批量修改文件名是高频中的高频,但很多人把逻辑写死了。我推荐的做法是把“要改哪些文件”和“改成什么名字”两件事分开:
# 06_batch_rename.py from pathlib import Path def rename_by_rule(directory: str, rule) -> list: """按rule函数重命名文件,返回(旧名, 新名)列表""" changes = [] for file in Path(directory).iterdir(): if not file.is_file() or file.name.startswith("."): continue new_name = rule(file.name) if new_name and new_name != file.name: target = file.with_name(new_name) # 避免覆盖已有文件 if not target.exists(): file.rename(target) changes.append((file.name, new_name)) return changes if __name__ == "__main__": import sys prefix = sys.argv[1] if len(sys.argv) > 1 else "backup_" renamed = rename_by_rule(".", lambda name: prefix + name) for old, new in renamed[:5]: print(f"{old} -> {new}")逻辑说明:rule是一个函数,接收旧文件名返回新文件名。这样不同的命名转换只需要换一个lambda,而不用重写整个函数。target.exists()先检查目标是否存在,避免静默覆盖重要文件。批量操作类代码,防呆比功能更重要。
参数说明:运行脚本时第一个参数是前缀,默认backup_。iterdir不递归子目录,如果你要递归改名,把iterdir换成rglob("*")。微软系文件系统上文件名大小写不敏感,把report.txt改成Report.txt可能会被视为同一文件,但不同系统行为不一,这里我建议统一走rename后自行确认。
4.4 小接口与自动化:用标准库起一个本地验证服务
有一些实例是关于“把逻辑包成接口”的。不用框架,http.server就够了,适合做本机的小工具验证:
# 07_http_tool.py import json from http.server import BaseHTTPRequestHandler, HTTPServer class Handler(BaseHTTPRequestHandler): def _send(self, payload: dict, code: int = 200): body = json.dumps(payload).encode("utf-8") self.send_response(code) self.send_header("Content-Type", "application/json; charset=utf-8") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) def do_GET(self): if self.path == "/health": self._send({"status": "ok"}) else: self._send({"error": "not found"}, 404) def log_message(self, fmt, *args): print(f"[http] {self.address_string()} {fmt % args}") if __name__ == "__main__": server = HTTPServer(("127.0.0.1", 8000), Handler) server.serve_forever()逻辑说明:HTTPServer绑定本机8000端口,Handler里重写do_GET分派路径。逻辑说明:_send统一处理JSON序列化和响应头,避免每个方法重复写send_response。这在本地起个接口让前端联调用,或测试一个解析逻辑是否支持HTTP调用时很快。log_message被重写为纯打印,避免默认往stderr写日志的噪音。
参数说明:("127.0.0.1", 8000)只监听本机,外部设备访问不到——如果你希望局域网内调试,把127.0.0.1改成0.0.0.0。端口被占用时会抛OSError,常见处理是换一个端口或先检查占用进程。
到这里,你已经从“跑通例子”进入“改例子”阶段。但真正让一堆经典实例变成多大价值的分水岭,是你会不会踩那些隐藏的坑。下一章我认为是全文最实用的部分——4类翻车场景,每个都是实际运行中我见过的。
5. 实例翻车重灾区:这4类坑先踩明白,能省一半调试时间
5.1 Windows下读UTF-8文件直接报编码错
现象:在Linux上写好的脚本换到Windows环境,用open("data.csv")直接读,抛UnicodeDecodeError。原因:Windows默认编码不是UTF-8,而是GBK系列;Python的open如果不指定encoding,会使用系统默认编码。解决:所有读写文本文件都显式传encoding="utf-8",写文件时加newline=""。
with open("data.csv", "r", encoding="utf-8", newline="") as f: ...这看着是小事,但几乎所有按“经典实例”抄的代码都会在这里翻车。还有一个隐蔽版本:open读没问题,但print中文时在Windows控制台又报UnicodeEncodeError。解决方式是把输出重定向到文件,或者先看控制台代码页。
5.2 路径硬编码:换一台机器所有相对路径全失效
现象:代码在A同学电脑上跑得好好的,打包给B同学就报FileNotFoundError。原因:代码里写了C:/Users/xxx/Desktop/data.csv或/home/user/data.csv,但每台机器的用户目录不同。解决:用pathlib.Path配合动态获取当前目录:
from pathlib import Path BASE_DIR = Path(__file__).resolve().parent data_path = BASE_DIR / "input" / "data.csv"__file__是当前脚本文件路径,resolve()解析成绝对路径,parent拿到脚本所在目录。这样数据文件只要和脚本保持相对位置,换机器就不会挂。如果你想要撤回确认,结合上一章的rename例子,返回旧新对照列表就是一种“后悔药”设计。
5.3 列表推导的坑:你改的可能是同一个对象
现象:一口气写了类似[ {} for _ in range(5) ],然后给第一项赋值,发现五项全变了。原因:[ {} for _ in range(5) ]在运行时会把同一个空字典引用复制五次,而不是创建五个新字典。解决:用独立构造:
# 正确写法:每轮循环都新建对象 result = [{"id": i, "data": []} for i in range(5)] # 错误写法:五个元素指向同一个字典 result = [{} for _ in range(5)] result[0]["x"] = 1 # 所有元素都多了"x"逻辑说明:可变对象被引用,不可变对象(数字、字符串)没有这个问题。这个坑在“生成器表达式转列表”时也容易出现。凡看到推导式里用{}、[],就要确认你确实创建了新对象。
5.4 第三方库版本差异:同样代码在不同机器上行为不同
现象:某段用requests的代码在旧机器上报“headers里不能有中文”,在新机器上正常。原因:不同版本的requests对HTTP头编码处理策略不同,默认的http.client版本也不同。解决:把依赖版本固定到requirements.txt里,并且用pip freeze生成当前可运行版本:
python -m pip freeze > requirements.txt后续换环境时python -m pip install -r requirements.txt。这不是玄学,是实录:跑经典实例时遇到最多的问题不是语法,而是“环境对不上”。如果你在网页上找到一段代码,务必先看它用到的库是什么版本。很多“经典实例”是哪年写的?那时requests到了哪个版本?这些信息往往在注释里没有,你得自己试。试错成本不高,装个指定版本就行。
6. 把每一段实例变成自己的“函数卡片”:一种可积累的验证技巧
到了最后一章,我想给你一个我一直在用的具体技巧——把经典实例从“脚本”升级成“函数卡片”。所谓函数卡片,就是每个例子最终留存的形态是一个函数加一组断言,而不是一段只能跑一次的main。
我举一个例子。第4章的to_number,当我需要复用时,我会把它单独放进一个模块,并直接写断言:
# my_utils/text_utils.py import re def to_number(value) -> float: if isinstance(value, (int, float)): return float(value) s = str(value).replace(",", "").replace("元", "").replace("¥", "").strip() return float(s) if re.fullmatch(r"-?\d+(\.\d+)?", s) else 0.0 def test_to_number(): assert to_number("1,299.00元") == 1299.00 assert to_number("¥3,500") == 3500.0 assert to_number("abc") == 0.0 assert to_number(42) == 42.0 print("全部断言通过")逻辑说明:assert直接验证函数行为,不需要额外装测试框架。当以后你重构这段代码,跑一下这个函数就知道有没有改坏。这一招对“汇总型代码”特别有价值:因为经典实例很多是网上抄的,自己不验证一遍,你根本不知道它在你的数据上会怎样。
我自己的习惯是:从网上下载或抄了一段经典代码,先不看它的输出,而是先想三个数——空输入、边界输入、脏输入。分别传给函数,看到的结果稳定才收进自己的工具模块。经过一个月,你攒下来的不是一百个脚本,而是十几个过亲手验证的函数卡片。这些卡片比那一百个实例本身更常用。
这个方向的真正价值在于:100例是别人的骨架,而“改造成自己的函数并用断言锁住行为”才是你的肌肉。希望这套思路能帮你在下个项目里少走弯路,也让你手里的那些经典实例真正长在自己的代码库上。
本文还有配套的精品资源,点击获取