简介:Python经典实例代码汇总是一份面向编程初学者与进阶开发者的案例合集,涵盖基础语法、数据类型操作、控制结构、函数与模块、面向对象编程、文件与异常处理、数据库交互、网络编程及数据可视化等常见场景,帮助读者通过具体代码快速掌握Python核心用法并提升排错能力。压缩包共包含96个py源文件,整体仅53KB,轻量易用;每个文件对应一个独立实例,适合按需查阅或系统练习。目前已有770人学习下载,内容兼具教学参考与工程实用价值。实例既包含变量定义、列表处理、条件循环等入门写法,也涉及SQLite/MySQL操作、Pandas与Matplotlib数据处理和图表生成等进阶主题,代码风格简洁且可读性强,可直接修改复用,是日常开发与备考复习的便捷实践手册。
1. 经典Python实例能做的事,比你想的宽得多
一百个Python实例听起来像是大学期末复习提纲,但真正跑过的人会告诉你,这些看起来零散的脚本,拆开之后其实是标准库、内置数据结构和若干核心编程范式的高密度集合。有人拿它应付考试,有人用它练手,我更多是把这一百个例子当成开发前的预演场:同样的文件处理、正则匹配、类与继承、多线程协作,在真实项目里只是包了一层业务壳。把这些实例打穿,等于把列表推导、装饰器、生成器、上下文管理器这些高频武器提前磨好了刃。
这篇文章的目标读者有三类:刚学完基础语法但写不出完整脚本的新手,想在面试前快速过一遍常用逻辑的求职者,以及需要一本随手能查的代码手册的在职开发者。我会按真实解决问题的顺序重新组织这100类例子,而不是机械地罗列。每段都给出可直接复制的完整代码、执行逻辑说明和参数调整方法,遇到容易翻车的点会单独标出来。你先不用管题目编号,跟着节奏把一个一个场景跑通,后半程自然会发现自己的手感完全不同。
2. 把这100例拆开看:它们到底在练什么
2.1 数据结构操作是绝对的主力区
一百个例子里,数量最多的永远是列表操作、字典操作、字符串变换和集合去重。这不是凑数,而是因为内置数据结构的熟练度直接决定写代码的速度。比如列表扁平化和嵌套字典取值这两个例子,前者考递归思维,后者考的是对默认字典和get方法的理解。
# 列表扁平化:把嵌套列表转成一维列表 def flatten(nested_list): result = [] for item in nested_list: if isinstance(item, list): result.extend(flatten(item)) # 递归处理子列表 else: result.append(item) return result sample = [1, [2, 3], [4, [5, 6]], 7] print(flatten(sample)) # 输出 [1, 2, 3, 4, 5, 6, 7]逻辑说明:isinstance用来判断当前元素是否还是列表,如果是就递归展开,否则直接收集到结果里。extend和append的区别在这里是关键——前者把整个子列表的成员逐个加入,后者会把子列表当作单个元素塞进去。
参数调整:如果你处理的层级深度固定为两层,可以用列表推导式[item for sublist in nested_list for item in sublist]一行解决,不需要递归。但如果深度不确定,递归版是唯一稳妥的方案。数据量大时递归有明显开销,可以用栈改写为迭代版。
嵌套字典取值的例子同样高频,它是很多真实项目中处理JSON配置的缩影:
# 多层嵌套字典安全取值,不抛KeyError import collections def deep_get(dictionary, keys, default=None): for key in keys: if isinstance(dictionary, dict): dictionary = dictionary.get(key, default) else: return default return dictionary config = {"api": {"timeout": 30, "retry": {"count": 3}}} print(deep_get(config, ["api", "retry", "count"])) # 3 print(deep_get(config, ["api", "missing", "key"], "fallback")) # fallback这段代码用循环逐层深入,每一层都通过get方法避免KeyError,拿不到就返回默认值。实际使用中我通常配合collections.defaultdict或json.loads的对象直接调用,效果一样。
2.2 文件与数据解析:路径处理、编码和格式转换
文件读写类题目约占两成,这是很多人容易忽略的部分。经典例子集中在:按行读取大文件、CSV读写、JSON序列化、glob批量匹配文件名。这些题目的意义在于让你建立一种条件反射——看见外部数据,立刻想到用标准库解决而不是手撸正则。
# 按行读取大文件,并统计每行长度 from pathlib import Path def analyze_line_lengths(file_path): path = Path(file_path) lengths = [] with path.open("r", encoding="utf-8") as f: for lineno, line in enumerate(f, 1): content = line.rstrip("\n") lengths.append((lineno, len(content))) return lengths # 用法示例:result = analyze_line_lengths("./data.log") # 返回 [(1, 42), (2, 17), ...]要点在于用Path对象统一管理路径,避免Windows与Linux分隔符差异。with语句保证文件句柄自动关闭,这是写入代码规范时最容易被考察的细节。enumerate(f, 1)让行号从1开始,和编辑器显示一致,排查问题时不至于数错行。编码参数必须显式声明,否则在含中文的文件上会踩UnicodeDecodeError的坑。
JSON序列化的例子也很典型,我一般会把它拆成dump和load两个方向分别写。实际开发中还会涉及ensure_ascii参数,控制输出中文是否变成\u转义序列。
2.3 面向对象与魔法方法:类、继承、封装
一百例里必然有一批关于类和对象设计的题目,这是Python区别于脚本化写法的分水岭。典型题目包括:自定义类实现__repr__和__str__、用__lt__支持对象排序、通过__call__让实例变成可调用对象、property装饰器实现计算属性。
# 用 __lt__ 让自定义对象支持排序 class Player: def __init__(self, name, score): self.name = name self.score = score def __lt__(self, other): return self.score > other.score # 分数高的排在前面 def __repr__(self): return f"Player({self.name}, {self.score})" players = [Player("alice", 88), Player("bob", 70), Player("carol", 95)] players.sort() print(players) # carol 95, alice 88, bob 70这里__lt__定义了小于行为的语义,我故意把分数高的称为“更小”,这样sort()默认升序排列时分数高者在前。没写__eq__时,相等性比较仍然基于默认对象身份,排序稳定性不会受到影响。
2.4 函数式工具:lambda、filter、map的高级替换方案
很多例子表面上在考lambda和内置函数,实际目的是让你理解函数作为一等公民的用法。但说实话,map和filter在现如今的代码里经常被列表推导式取代,只有在结合多线程或复杂数据流水线时才显得不可替代。更值得关注的是functools模块下的partial和reduce,以及operator模块的itemgetter。
# partial固定参数生成专用函数 from functools import partial def power(base, exponent): return base ** exponent square = partial(power, exponent=2) cube = partial(power, exponent=3) print(square(5)) # 25 print(cube(3)) # 27partial的威力在于把一个多参数函数转换成单参数或固定参数的版本,在回调函数、GUI事件绑定和处理函数中非常实用。例如在tkinter或某些任务队列框架里,需要传一个无参函数,partial就是救命稻草。
3. 编程语言基础之外:这些例子真正想教会你的思维
3.1 递归与动态规划的入门手感
递归题几乎必考,常见场景涵盖阶乘、斐波那契、汉诺塔、走迷宫。这些例子的价值不在算法本身的复杂度,而在于培养一种自顶向下的拆解思维:把大问题分解成结构相同的子问题。
# Fibonacci数列三版对比:递归 / 记忆化 / 迭代 from functools import lru_cache def fib_recursive(n): if n < 2: return n return fib_recursive(n - 1) + fib_recursive(n - 2) @lru_cache(maxsize=None) def fib_memo(n): if n < 2: return n return fib_memo(n - 1) + fib_memo(n - 2) def fib_iterative(n): a, b = 0, 1 for _ in range(n): a, b = b, a + b return a递归版本写起来最贴近数学定义,但n达到35时已经明显变慢;记忆化版本通过缓存中间结果把复杂度降到线性;迭代版本空间占用最小。实际面试中,通常要求你把三者的区别说清楚,并用迭代法手写一遍。
3.2 排序算法的完整实现与复杂度实测
排序类经典例子包括冒泡、选择、插入、快速排序、归并排序。如果是在学习阶段,每个算法的手写实现是必须的,它帮你理解比较、交换、分治这些底层操作。但如果在生产代码里手写快速排序,那就属于重复造轮子——直接用内置sorted和list.sort。检测点通常在于:你是否知道内置排序的稳定性和时间复杂度。
# 手写快速排序(教学演示用途),生产环境请使用内置sort() def quicksort(arr): if len(arr) <= 1: return arr pivot = arr[len(arr) // 2] left = [x for x in arr if x < pivot] middle = [x for x in arr if x == pivot] right = [x for x in arr if x > pivot] return quicksort(left) + middle + quicksort(right) print(quicksort([3, 6, 8, 10, 1, 2, 1]))这个写法用了三个列表推导式,代码容易读但额外占用内存。工程场景中若处理海量数据,原地partition的Lomuto或Hoare方案更合适。我在做数据清洗时就吃过亏——递归深度过大触发了RecursionError,后来发现直接使用sorted(arr, key=lambda...)更稳妥。
3.3 字符串匹配:正则表达式与经典算法对照
正则表达式题目占据了字符串类别的半壁江山。手机号校验、邮箱提取、文本清洗、HTML标签过滤,这些案例背后都是同一条学习路径:先理解字符类、量词、分组、捕获与非捕获,再逐步过渡到贪婪与懒惰匹配。
# 提取日志中的时间与IP地址 import re log_line = "2025-01-12 08:23:45 ERROR 192.168.1.100 connection timeout" pattern = r"(?P<date>\d{4}-\d{2}-\d{2})\s(?P<time>\d{2}:\d{2}:\d{2})\s\w+\s(?P<ip>\d+\.\d+\.\d+\.\d+)" match = re.search(pattern, log_line) if match: print(match.group("date")) # 2025-01-12 print(match.group("time")) # 08:23:45 print(match.group("ip")) # 192.168.1.100命名分组(?P<name>...)让代码可读性提升一个档次,抓取大量字段时不需要记忆数字索引。re.search和re.match的区别也常被忽略:match只从字符串开头匹配,search扫描整个字符串。实战里大多数情况应该用search。
但正则不是万能的。经典字符串匹配算法KMP和Boyer-Moore偶尔出现在实例中,它们的意义是让你理解Python的in操作符和str.find为什么会高效——底层经过C语言优化,工程上几乎不需要手动实现。
3.4 异常处理与断言:为代码穿防弹衣
异常处理类题目的数量虽然不多,但含金量很高。考察点集中在try-except-else-finally的顺序、自定义异常类、with与上下文管理器的配合。
# 自定义异常和完整的异常处理链 class ConfigError(Exception): pass def load_config(path): try: with open(path, "r", encoding="utf-8") as f: data = f.read() except FileNotFoundError: raise ConfigError(f"配置文件不存在: {path}") from None except PermissionError: raise ConfigError(f"没有权限读取: {path}") from None else: if not data.strip(): raise ConfigError("配置文件为空") return data # 调用端写法 try: cfg = load_config("./app.ini") except ConfigError as e: print(f"加载失败: {e}") else: print("配置解析成功") finally: print("无论成败,都会执行到这里")from None用来隐藏原始异常链,让自定义异常信息更干净;else块只有try里没异常才会执行;finally适合放释放资源的动作。这套模式在真实服务里非常常见,许多框架的配置加载模块就是这种结构。
4. 手写这100例的五个台阶:从抄代码到改业务
4.1 第一台阶:照抄并跑通,理解每一行的执行顺序
很多初学者第一次打开实例集都会犯同一个错误——从头到尾读代码,而不是执行代码。读代码建立不了肌肉记忆,报错信息才是真正的老师。我的做法是:新建一个叫examples_practice的目录,每个题目单独一个.py文件,不管三七二十一先跑一遍,然后把所有print语句前后调换,观察输出变化。
# 创建练习目录并初始化 mkdir examples_practice cd examples_practice python -m venv venv source venv/bin/activate # Windows下用 venv\Scripts\activate pip install pytest ipython这一步的验收标准不是输出正确,而是你能预判输出。如果每句代码执行后变量值的变化你都能在脑子里过一遍,第一个台阶才算站稳。
4.2 第二台阶:改参数,观察边界行为
把代码中的固定数值改掉,设计边界输入,是第二阶段核心任务。比如列表去重程序,输入空列表、全重复列表、包含不可哈希元素的列表,看会发生什么。排序程序,输入逆序数组、全等数组、包含None的数组,看会不会报错。
# 带边界的列表去重 def deduplicate(items): seen = set() result = [] for item in items: if item not in seen: seen.add(item) result.append(item) return result # 边界测试 print(deduplicate([])) # [] print(deduplicate([1, 1, 1, 1])) # [1] # print(deduplicate([1, [2], [2]])) # 报错,列表不可哈希看到不可哈希的报错后,你应该想到改用不可变类型包装,比如转换成元组再入集合。这个台阶的核心是让你养成一个习惯——任何代码都追问一句“如果输入变了会怎样”。
4.3 第三台阶:用pytest把实例改造成自动化测试
把100个例子写成测试用例,是内化程度最高的一步。不需要为每个例子建立复杂框架,只要用pytest的@pytest.mark.parametrize把输入输出对列出来即可。
# 把去重代码改造成测试驱动形式 import pytest def dedup(items): return list(dict.fromkeys(items)) @pytest.mark.parametrize("input_data,expected", [ ([], []), ([1,2,2,3], [1,2,3]), (["a","b","a"], ["a","b"]), ]) def test_dedup(input_data, expected): assert dedup(input_data) == expecteddict.fromkeys这个写法利用了字典键唯一性,既保留顺序又完成去重,而且比set版本更直观。注意它要求元素可哈希,和set方案的限制一致。把参数化用例写好,日后往代码库里加逻辑时,跑一遍就知道有没有破坏原有行为。
4.4 第四台阶:把两个例子拼成一个复合任务
实例与实例之间不是孤立的。比如“读文件”配“正则提取”配“字典统计”,三个例子拼在一起就成了一段日志分析脚本。这正是真实项目的缩小版——把单一技能组合成业务流程。
# 组合示例:统计日志中每种错误级别出现次数 import re from collections import Counter def count_log_levels(log_path): pattern = re.compile(r"\b(INFO|WARNING|ERROR|DEBUG)\b") levels = [] with open(log_path, "r", encoding="utf-8") as f: for line in f: match = pattern.search(line) if match: levels.append(match.group(1)) return Counter(levels) # result = count_log_levels("./server.log") # 返回 Counter({'INFO': 320, 'ERROR': 15, ...})组合阶段最容易踩的坑是编码和性能。逐行读取虽然省内存,但re.compile必须放在循环外,否则每个字节流都重新编译正则,性能直接掉一个数量级。
4.5 第五台阶:改写生产风格,加上类型注解和docstring
最后一个台阶是让代码具备工程气质:加类型注解、写文档字符串、拆分子函数。这并非形式主义,而是当代码量超过500行后,类型注解带来的IDE提示和静态检查收益非常大。
def parse_http_status(line: str) -> tuple[int, str] | None: """ 从日志行里提取HTTP状态码与说明文本。 返回 (code, text) 元组,匹配失败返回 None。 """ pattern = re.compile(r"\" (\d{3}) ([A-Za-z ]+)\"") match = pattern.search(line) if match: return int(match.group(1)), match.group(2).strip() return Nonetuple[int, str] | None是Python 3.10+的写法,旧版本需要改成Optional[Tuple[int, str]]。这一步能让你从“能跑”过渡到“能维护”,也是区分初学者和一线开发者的标志之一。
5. 实例翻车常见问题排查:现象、原因、解法
5.1 中文输出乱码
现象:控制台打印中文显示为â\x80\x99之类的乱码,或文件里写中文后打开是乱码。
原因:终端编码与Python字符串编码不一致。Windows默认GBK,而Python 3源码默认UTF-8;文件写入时未指定encoding参数,使用平台默认编码所致。
解决:统一显式声明编码。打印时先检查终端是否支持UTF-8,文件读写一律传入encoding="utf-8"。写入文本文件时还可以加errors="replace"防止中间遇到非法字符直接中断。
5.2 递归深度超过限制导致RecursionError
现象:运行递归题目时抛出RecursionError: maximum recursion depth exceeded,有时提示comparison相关。
原因:默认递归上限约1000层。某些算法(如未优化的递归快排、朴素版的递归遍历目录)在数据规模稍大时就触顶。
解决:方法一,改用迭代或尾递归思路重写;方法二,临时提高递归上限,用sys.setrecursionlimit(10000),但只适合放心算法不会无限递归的场合;方法三,使用functools.lru_cache减少重复递归分支的计算量。最稳妥的方案是重构算法本身,而不是调高阈值。
5.3 列表在循环中删除元素导致漏删
现象:想要过滤掉列表里所有符合条件的元素,用for循环遍历加remove,结果总有几个元素没删掉。
原因:remove会改变列表长度和索引,循环内部索引自动前进,跳过了被删除元素的后一个元素。这是经典迭代过程中修改容器的反模式。
解决:使用列表推导式重建列表,filtered = [x for x in items if condition(x)]。如果确实需要原地修改,用倒序遍历索引的方式删除;或者先复制一份列表用于遍历,在原列表上做删除操作。这个问题的升级版出现在“一行代码实现列表去重并保持顺序”的题目中,理解了原因就能看穿所有变体。
5.4 可变默认参数带来的幽灵状态
现象:定义一个带默认列表参数的函数,多次调用后结果不断累积,第一次调用返回的结果也被后续调用修改。
原因:Python函数的默认参数在定义时只创建一次,之后每次调用都复用同一个对象。列表是可变类型,累积修改还在同一个对象上。
解决:默认参数不能写def func(items=[]),必须写成def func(items=None),函数体内判断items = items if items is not None else []。这是Python面试中最高频的陷阱之一,一百个实例里也常见它的身影。
5.5 全局变量和局部变量混淆导致UnboundLocalError
现象:函数内先使用一个变量,再给它赋值,运行时抛出UnboundLocalError: local variable 'x' referenced before assignment。
原因:Python编译器把函数内被赋值的变量视为局部变量,即使外层存在同名全局变量,函数内读取时也按局部变量处理,导致赋值前引用出错。
解决:若确实要读写全局变量,函数内用global声明;若只想读取,改名为局部变量或作为参数传入。更好的做法是设计函数时完全避免依赖全局状态,把所有数据通过参数传入、返回值带出。很多经典实例没有显式教这个点,但它和闭包、装饰器的理解直接挂钩。
6. 把100例变成你查得快、改得动的私房代码库
走到这一步,你已经把经典实例逐行读过、跑过、改过了。接下来要做的不是找新题,而是建立自己的检索索引和提炼模板。我自己的习惯是每学习一个实例就记录三个要素:适用场景一句话、核心代码十行内、踩过的坑一条。日积月累形成一份私房速查表,调试时翻它比翻搜索引擎快得多。
# 我的私房记录格式示例:用字典模拟速查索引 cheatsheet = { "列表扁平化": { "场景": "JSON数据里嵌套数组需要拍平", "代码": "def flatten(lst): ...", "坑": "深度超过1000会递归超限,用迭代版", }, "字典安全取值": { "场景": "多层配置项读取,缺失时给默认值", "代码": "deep_get(d, keys, default)", "坑": "中间层不是字典形态时要返回default", }, "日志统计": { "场景": "分析服务端日志的关键字频率", "代码": "Counter + 逐行读取", "坑": "正则模式要compile在循环外", }, }这种“场景-代码-坑”的三段式,比章节目录更适合检索。遇到新需求时先看场景命中了哪条,直接跳到对应代码块改参数,而不是重新从零写。
对已经有一定经验的朋友,我可以分享一个小习惯:每个实例都试着追问两个问题,一个是“这个函数如果输入百万级数据会怎样”,另一个是“如果并发调用同一个对象的方法会如何”。后一个问题特别容易暴露共享状态带来的隐患。很多实例在设计时没有考虑线程安全,但你在真实环境里用的时候,这段代码可能同时被多个任务调用,一旦状态互相干扰就是线上翻车。就算当下不写并发项目,保持这个疑问也能帮你提前避开很多隐藏炸点。
最后一条经验是真实的血泪教训:初学阶段不要追求一天刷完几十道题,慢一点没关系,但每道题跑通之后必须把参数改乱几次。只有亲手制造过报错,你才知道报错信息在说什么。修改参数这件事相当于给自己制造可控的故障现场,练到的排错能力才是真正能带去工作的部分。
这套路子走下来,一百个经典实例就不再是题目集,而是一组你可以随时调用、改得动、也能解释清楚的武器库。遇到新场景不用慌,先想它属于哪一类,再翻自己的笔记确认细节。希望帮到你,祝你写每个脚本都是顺手的。
本文还有配套的精品资源,点击获取