好家伙,妥妥的 Python Master
别一上来就想写解释器、造框架。对绝大多数人来说,“Python Master”大概是这些能力的综合:
业务来了,脑子里自然会冒出几个 Python 解决方案,还能评估哪个最合适 看别人一坨代码,不至于一脸懵,能快速找到问题点,手起刀落重构 碰到性能瓶颈、线上异常,不会只会加日志瞎猜,知道怎么定位、怎么验证 有自己的代码习惯和工程套路,不是到处 copy 粘贴
你看,其实跟我以前写数据库、MQ那堆排查经历是一个路子:把细节吃透,踩过足够多坑,自然就“像那么回事”了
下面我就按“从能写,到写好,再到玩明白”的节奏来聊聊。
基础要会到什么程度,才算像样?
很多人学 Python 的路线是:刷语法题、做选择题……说实话,这种方式只够你写“能跑的脚本”,离“Master”差得远。
我觉得基础阶段,至少要做到:
基本语法写起来不需要查:列表、字典、集合、切片、函数、类 理解“值”和“引用”的区别,别天天被可变类型坑 会用虚拟环境、依赖管理,知道 pip/venv/poetry 是干嘛的 能看懂别人稍微正经一点的项目结构,不迷路
随便给一段“基础里带点进阶味”的小例子,你可以对照自己现在的写法看看差在哪:
from pathlib import Path
from contextlib import contextmanager
from typing import Iterator, List
@contextmanager
defopen_log(path: Path) -> Iterator[Iterator[str]]:
"""一个简单的上下文管理器包一层,顺便加点日志。"""
print(f"[INFO] Reading log from {path}")
f = path.open("r", encoding="utf-8")
try:
yield f
finally:
print("[INFO] Done")
f.close()
deffind_error_lines(path: str) -> List[str]:
log_path = Path(path)
ifnot log_path.exists():
raise FileNotFoundError(f"log file not found: {path}")
errors: List[str] = []
with open_log(log_path) as f:
for idx, line in enumerate(f, start=1):
if"ERROR"in line:
errors.append(f"#{idx}: {line.rstrip()}")
return errors
if __name__ == "__main__":
for line in find_error_lines("app.log"):
print(line)
这里面其实就揉进去了不少“以后你肯定会用到”的东西:
pathlib.Path代替裸字符串contextmanager自己封装小工具typing提前养成写类型标注的习惯enumerate这种常见的 Pythonic 写法
你要是读起来挺顺、还能自己改造,那基础差不多就够用可以往上走了。
别只刷题,多写点真有用的小东西
我身边所有写 Python 写得不错的人,几乎都有一堆“杂七杂八的小脚本”:批处理 Excel、日志分析、接口巡检、小爬虫、自动化部署脚本…… 这些东西有两个好处:
立刻能解决你工作里的真问题,反馈快 会逼着你接触到文件操作、网络请求、异常处理、定时任务这些“真实世界”的细节
比如,你可以搞一个“接口健康巡检”的小脚本,挂到 crontab 上,每隔几分钟跑一下:
import time
from typing import Dict
import requests
URLS: Dict[str, str] = {
"user": "https://api.example.com/user/health",
"order": "https://api.example.com/order/health",
}
defcheck_services(timeout: float = 2.0) -> Dict[str, bool]:
result: Dict[str, bool] = {}
for name, url in URLS.items():
try:
resp = requests.get(url, timeout=timeout)
result[name] = resp.status_code == 200
except requests.RequestException:
result[name] = False
return result
defmain() -> None:
statuses = check_services()
now = time.strftime("%Y-%m-%d %H:%M:%S")
for name, ok in statuses.items():
status_text = "OK"if ok else"DOWN"
print(f"[{now}] {name}: {status_text}")
if __name__ == "__main__":
main()
这个东西很简单,但里面几乎涵盖了一大堆常见能力:
网络请求、超时时间、异常捕获 命令行脚本入口 打印结构化一点的日志
你多写几类这种脚本,其实思路就慢慢往“工程化”方向靠了——这跟我以前排查 Feign 超时、各种网络问题时的思维方式是一致的
从“写得能跑”到“写得优雅”:进阶语法要上场了
等你不再紧张“语法会不会写错”的时候,就可以稍微“讲究”一点了。Python 的强大,很多时候就是体现在这种小语法糖和抽象能力上。
几个一定要熟练用的东西:
列表推导式 / 字典推导式 生成器( yield)、惰性计算with上下文管理装饰器(简单场景先会用,后面再研究原理)
来看一个处理大文件的例子,假设你需要从一个巨大的日志文件里统计某接口的平均耗时,如果全读到内存里肯定爆:
from pathlib import Path
from typing import Iterable, Iterator
defread_lines(path: Path) -> Iterator[str]:
with path.open("r", encoding="utf-8") as f:
for line in f:
yield line.rstrip("\n")
deffilter_api_log(lines: Iterable[str], api_name: str) -> Iterator[str]:
for line in lines:
if api_name in line:
yield line
defparse_cost(lines: Iterable[str]) -> Iterator[float]:
for line in lines:
# 假设日志里有这样的片段 cost=123.45ms
if"cost="notin line:
continue
try:
part = line.split("cost=")[1].split("ms")[0]
yield float(part)
except (IndexError, ValueError):
continue
defavg_cost(path: str, api_name: str) -> float:
log_path = Path(path)
lines = read_lines(log_path)
api_lines = filter_api_log(lines, api_name)
costs = list(parse_cost(api_lines))
return sum(costs) / len(costs) if costs else0.0
if __name__ == "__main__":
print(avg_cost("app.log", "/api/order/create"))
这里的套路你体会一下:
每个函数都做一件很小的事 通过生成器一环套一环,内存占用可控 逻辑线性、清晰,测试也很方便
你以后做 ETL、日志分析、数据清洗,这一套都能复用。
工程化:项目结构、日志、测试一个都别躲
“写脚本”和“写项目”的区别,最大的其实在于:你要开始为未来的自己(和同事)负责了。
最简单的一个项目结构,可以从这样开始:
my_project/
my_project/
__init__.py
config.py
models.py
services/
__init__.py
user_service.py
order_service.py
tests/
test_user_service.py
pyproject.toml / requirements.txt
main.py
然后,最起码要有像样的日志,而不是乱 print。比如:
import logging
from logging.handlers import RotatingFileHandler
from pathlib import Path
defsetup_logger() -> logging.Logger:
log_dir = Path("logs")
log_dir.mkdir(exist_ok=True)
logger = logging.getLogger("app")
logger.setLevel(logging.INFO)
handler = RotatingFileHandler(
log_dir / "app.log",
maxBytes=5 * 1024 * 1024,
backupCount=3,
encoding="utf-8"
)
formatter = logging.Formatter(
"%(asctime)s [%(levelname)s] %(name)s - %(message)s"
)
handler.setFormatter(formatter)
logger.addHandler(handler)
# 控制台也打一份
console = logging.StreamHandler()
console.setFormatter(formatter)
logger.addHandler(console)
return logger
logger = setup_logger()
defcreate_user(username: str) -> None:
logger.info("creating user: %s", username)
# TODO: 你的业务逻辑
# 如果抛异常,日志会带堆栈
logger.info("user created: %s", username)
if __name__ == "__main__":
create_user("alice")
再往上,一个合格的 Python 项目一定要有测试。哪怕先从最简单的 pytest 开始:
# tests/test_math_utils.py
from my_project.math_utils import add
deftest_add():
assert add(1, 2) == 3
你会发现,当你开始写测试之后,代码结构会被迫变得更清晰,这一点在我排查各种主从同步、网络奇怪 bug 的时候体现得特别明显:可测的代码,问题定位永远更快
学会跟 Bug 和 性能问题“谈判”
想走到“Master”这个段位,debug 和性能分析一定是绕不过去的。
日志 + 断点调试
logging用好,日志级别分清楚(DEBUG/INFO/WARNING/ERROR)用 pdb或者 IDE 自带的断点,别全靠print
举个极简的 pdb 用法:
import pdb
deftricky_calc(x: int, y: int) -> int:
pdb.set_trace() # 程序跑到这里会停下来
return x // y
if __name__ == "__main__":
tricky_calc(10, 0)
跑的时候你可以一步步看变量、执行路径,比瞎猜强太多。
性能优化一点点来
不要一上来就想着用 C 扩展、写 Rust;多数时候,你只要:
换个数据结构(列表 -> 集合、字典) 把重复计算的逻辑抽出去缓存 用生成器减少内存峰值 合理地用多进程/多线程/协程
就够用了。
例如简单测试某段代码的耗时:
from time import perf_counter
defslow_func():
return sum(i * i for i in range(10_000_000))
if __name__ == "__main__":
t0 = perf_counter()
slow_func()
t1 = perf_counter()
print(f"cost: {t1 - t0:.3f}s")
你可以写个小装饰器,把这种耗时统计封装掉,挂到关键路径上看一下。
并发和异步:别急着全都学,但要知道有什么
Python 的并发有点绕,但对“Master”来说,至少要搞清楚三个东西各自适合啥场景:
threading:IO 密集型任务可以用,多数 Web 框架背后都会用到multiprocessing:CPU 密集型,比如大规模计算asyncio:大量 IO 并发,比如高并发爬虫、网关、IM 服务之类
给你丢一段很简单的 asyncio 风格代码,感受一下写法:
import asyncio
from typing import List
import aiohttp
asyncdeffetch(session: aiohttp.ClientSession, url: str) -> int:
asyncwith session.get(url, timeout=2) as resp:
await resp.text()
return resp.status
asyncdefmain(urls: List[str]) -> None:
asyncwith aiohttp.ClientSession() as session:
tasks = [fetch(session, u) for u in urls]
for coro in asyncio.as_completed(tasks):
status = await coro
print("got status:", status)
if __name__ == "__main__":
urls = [
"https://httpbin.org/get",
"https://example.com",
"https://python.org",
]
asyncio.run(main(urls))
真到你要做这种活的时候,再系统地补一遍协程、事件循环的知识,其实效率会更高。
想往“Master”靠拢,有几个习惯特别关键:
多看高质量源码
标准库: pathlib、logging、functools、itertools常用三方:FastAPI、Requests、Pydantic 这种
写点自己的轮子,但别乱造比如自己做个简单的配置管理、小型 ORM、轻量任务调度器,主要是练设计能力
记录踩坑过程我很多对底层的理解,其实都是从一次次“奇葩 bug 排查记录”里冒出来的
到这个阶段,你回头看自己第一年写的 Python,多少都会想删库跑路——那大概率说明你真的在进步。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB