014、注释与 PEP8:写出让人读得懂、AI 抄得对的 Python 代码

发布时间:2026/6/22 22:57:10
014、注释与 PEP8:写出让人读得懂、AI 抄得对的 Python 代码 014、注释与 PEP8写出让人读得懂、AI 抄得对的 Python 代码上周五晚上十一点我被一个线上告警电话从床上拽起来。一个跑了三个月的数据处理脚本突然报错日志里只有一行“IndexError: list index out of range”。我打开代码看到这样的注释# 处理数据foriinrange(len(data)):resultdata[i][2]# 取第三个字段三个月前写这段代码的人已经离职了。我盯着那个魔法数字“2”不知道它代表什么字段。更糟糕的是data 的结构在某个版本更新后变了索引 2 变成了空值。如果当初注释写清楚“这是用户手机号”或者用常量代替魔法数字我至少能少花两小时定位问题。这个场景让我意识到注释和代码规范不是写给编译器看的是写给三个月后的自己、接手你代码的同事、以及越来越常见的 AI 代码助手看的。AI 模型训练时大量抓取 GitHub 上的 Python 代码如果你的代码注释混乱、风格不一致AI 抄出来的代码也会带着同样的毛病。注释别写“是什么”写“为什么”很多初学者喜欢写这种注释xx1# 把x加1这种注释等于没写。代码本身已经表达了“做什么”注释应该解释“为什么这么做”。我踩过最深的坑是一个汇率转换函数defconvert_currency(amount,rate):# 这里乘以100再除以100是为了避免浮点数精度问题# 之前线上出现过0.10.2不等于0.3的bugreturnround(amount*rate*100)/100如果没有那段注释后来维护的人可能会觉得“多此一举”直接删掉乘除操作然后线上又炸一次。写注释的黄金法则是假设读代码的人知道 Python 语法但不知道你的业务逻辑和踩过的坑。比如# 坏注释foriinrange(24):# 循环24次# 好注释forhourinrange(24):# 遍历一天24小时生成每小时的数据快照再比如处理用户输入时# 坏注释iflen(name)0:# 检查名字长度# 好注释iflen(name)0:# 防止空字符串导致后续数据库查询报错之前遇到过空用户名写入失败文档字符串给函数写说明书单行注释解决的是“这里为什么这么写”文档字符串docstring解决的是“这个函数怎么用”。我见过最离谱的代码是一个 200 行的函数没有任何文档字符串参数名全是 a、b、c。三个月后原作者自己都看不懂了。标准写法是这样的defcalculate_discount(price,user_level,is_vipFalse): 根据用户等级和VIP状态计算折扣后的价格 参数: price (float): 原始价格单位元 user_level (int): 用户等级1-5级 is_vip (bool, optional): 是否为VIP用户默认False 返回: float: 折扣后的价格 示例: calculate_discount(100, 3, True) 85.0 discount_map{1:0.9,2:0.85,3:0.8,4:0.75,5:0.7}base_discountdiscount_map.get(user_level,1.0)ifis_vip:base_discount*0.95returnround(price*base_discount,2)注意那个示例部分用 doctest 格式写。这样既能当文档又能直接跑测试。我习惯在写完函数后立刻补上文档字符串因为这时候思路最清晰。等过几天再补大概率会忘掉某些边界情况。PEP8不是教条是团队协作的底线PEP8 是 Python 的官方编码规范但很多人把它当成“必须遵守的规则”其实更应该理解为“减少沟通成本的约定”。我见过最极端的例子一个团队里有人用 2 空格缩进有人用 4 空格有人用 Tab合并代码时 diff 全是缩进变化真正的逻辑改动反而被淹没了。几个最容易踩坑的点行长度PEP8 建议每行不超过 79 字符。别笑我见过有人写一行 300 字符的列表推导式调试时根本看不清哪里报错。如果一行太长用括号隐式换行# 别这样写resultsome_function(param1,param2,param3,param4,param5,param6,param7)# 这样写resultsome_function(param1,param2,param3,param4,param5,param6,param7)空行函数之间用两个空行类方法之间用一个空行。这不是强迫症是视觉分组。想象一下你在一堆文字里找某个函数定义空行就是路标。导入顺序标准库、第三方库、本地模块每组之间空一行。这样一眼就能看出依赖关系importosimportsysimportrequestsimportpandasaspdfrommyproject.utilsimportformat_date变量命名别用单字母变量名除非是循环计数器。我见过一个函数里用了 a、b、c、d、e、f 六个单字母变量调试时根本分不清谁是谁。好的命名应该让人一看就知道这个变量存的是什么# 坏dpd.read_csv(data.csv)fd[d[age]18]rf.groupby(city).mean()# 好raw_datapd.read_csv(data.csv)adult_dataraw_data[raw_data[age]18]city_statsadult_data.groupby(city).mean()让 AI 抄得对注释就是训练数据现在很多团队用 AI 辅助写代码比如 GitHub Copilot、Cursor 之类的工具。这些 AI 模型训练时大量抓取公开代码库如果你的代码注释清晰、风格规范AI 生成代码时就会模仿这种风格。反过来如果你的代码全是魔法数字、没有注释、命名混乱AI 也会“学坏”。我做过一个实验在一个函数里写清楚文档字符串和注释然后让 AI 补全类似功能的函数。AI 生成的代码自动带上了文档字符串和类型注解。另一个函数我故意不写注释AI 生成的代码也光秃秃的。所以写规范的注释和代码不仅是为了人类同事也是在“训练”你身边的 AI 助手。你写得越好AI 给你的建议就越靠谱。个人经验养成肌肉记忆说了这么多最后分享几个我自己的习惯写完函数立刻写文档字符串哪怕只写一行。等回头再补大概率会忘。魔法数字必须加注释或改成常量。我吃过太多亏了现在看到代码里有裸数字就浑身难受。用 linter 自动检查。我本地配了 flake8 和 black保存代码时自动格式化。别靠肉眼检查 PEP8人眼会疲劳机器不会。注释里可以写“为什么不用另一种方案”。比如“这里没用列表推导式是因为可读性太差”这种注释能帮后来的人避免重复踩坑。代码是写给人看的顺便让机器执行。注释和规范就是让“人”这部分更顺畅。三个月后的你会感谢现在认真写注释的自己。