
假设你接手了一个项目打开主模块看到一个函数def process(data):内部用type(data) str判断分支异常被print掉后返回None没有任何注释。你心里清楚这段代码能跑但没人敢改。提升Python代码质量不是追求花哨特性而是让代码在六个月后依然能被人迅速理解、安全修改。这里有五个技巧每一个都经过了实战锤炼它们不依赖高级框架只依赖你写下一行代码时的意识。让类型注解成为接口文档很多人觉得类型注解是给解释器看的于是写上def add(a, b)就完事。实际上解释器根本不关心注解类型注解是给下一个读你代码的人看的包括六个月后的你自己。一个清晰的函数签名应当让人不读函数体就能知道输入输出def calculate_total(prices: list[float], discount: float 0.0) - float:比def calculate_total(prices, discount0.0)多付出的只是几个字符省下的却是对方阅读整个函数逻辑的时间。真正让类型注解发挥威力的是结合typing模块里的Optional、Union、Callable。比如def find_user(user_id: int) - Optional[User]看到这个签名调用者立刻明白返回可能为None必须处理空值。好的函数签名应该让人一眼就知道输入输出的可能形态而不是靠猜。但注意不要走极端把每一个变量都标上Dict[str, List[Tuple[int, str]]]这种过度设计会让代码变成噪音。注解的黄金法则是标注公共接口、标注容易歧义的边界让类型系统替你挡住明显错误。用数据类替代字典和元组业务逻辑中最常见的坏味道是把一组相关联的字段塞进字典比如user {name: 张三, age: 30}。字典灵活但也放纵了错误你写user[nam]时运行前不会有任何提示。数据类让业务对象清晰而不是塞进字典。用dataclass定义User字段名、默认值、类型一目了然。from dataclasses import dataclass dataclass(frozenTrue) class User: name: str age: int email: str 这样做的收益远超减少打字。frozenTrue让实例不可变避免在某个角落被意外修改加上类型注解后IDE的补全和静态检查工具能直接发现拼写错误。更重要的是数据类强制你思考对象的本质属性而不是用一个松散的字典应付一切。当你发现一个字典里同时存着用户信息和订单信息时就该拆成两个独立的数据类。能用标准库解决就不要发明新轮子。数据类就是标准库给你的轮子它帮你的代码建立起结构而不是靠注释维持秩序。上下文管理器资源管理的一等公民每个Python开发者都知道with open(...) as f但到了数据库连接、线程锁、临时目录时很多人又退回了try...finally甚至忘记释放资源。忘记关闭文件是小事关闭数据库连接是事故。资源管理不该依赖人的记忆力而应该依赖语法结构。自定义一个上下文管理器非常简单实现__enter__和__exit__即可。更简洁的方式是用contextlib.contextmanager把一个生成器函数变成上下文管理器from contextlib import contextmanager contextmanager def managed_resource(args, kwargs): res acquire(args, kwargs) try: yield res finally: release(res)__exit__方法的返回值有一个容易被忽略的特性返回True会吞掉异常返回False则继续抛出。上下文管理器的核心是责任分离获取资源和释放资源的逻辑被封装在最合适的地方调用者只用关心业务。比如实现一个timeout上下文管理器让一段代码限定执行时间或实现一个事务上下文管理器异常时自动回滚。当你发现try...finally代码块在多个函数里重复出现时就是把它提升为上下文管理器的信号。这能减少嵌套层级让主逻辑浮出水面。异常处理的艺术不要吞掉异常我见过最糟的代码不是没有异常处理而是except Exception: pass。这行代码让所有问题消失得无声无息直到用户反馈“数据怎么不对”。except Exception: pass是代码质量的头号杀手。它掩盖了错误类型吞掉了堆栈信息让下一次调试变成考古。正确的做法是捕获你预期会发生的异常并且只捕获那一种。try: value int(raw) except ValueError: return default_value如果需要向上层报告务必保留异常链。用raise ... from ...把底层异常附加到新异常上这样日志里既能看到“数据库连接失败”也能看到引起失败的根本原因是“socket超时”。异常链是调试者的救命稻草。自定义异常类也是提升可读性的利器class InsufficientFundsError(Exception)比通用Exception更能传达业务语义。底层函数应该抛出具体异常上层才做决策。千万别在底层捕获所有异常后返回None那等于把判断错误类型的责任推给了调用方而调用方可能根本不知道该不该继续。自动化检查让工具替你操心代码质量如果只靠代码评审时人眼去抓那是浪费生命。质量不是靠人手检查出来的而是靠规则固化出来的。一个健康的Python项目应当标配三样工具ruff做lint和格式化mypy做静态类型检查pytest跑单元测试。这些工具不是装点门面而是把常见的坏味道自动化地拦截在提交之前。ruff能发现未使用的导入、过长的函数、不一致的引号mypy能在你调用函数时传入错误类型的情况下立刻标红。把这些检查集成到pre-commit钩子里每次提交前自动运行。自动化工具最大的价值不是抓bug而是让团队形成统一的代码风格减少无意义的争论。当所有人都用同样的格式化和检查规则代码diff里就不会出现因为缩进或换行引起的噪音。测试也不是可有可无的——给核心逻辑写三个测试胜过十段注释。pytest的优势在于简洁断言而且失败时能清晰显示差异。把边界条件、异常分支都写成测试用例这些测试就是你重构时的安全网。回到开头那个def process(data)。如果你能给它加上类型注解把内部逻辑改成数据类操作用上下文管理器管理外部资源在合适的位置捕获明确异常并让这些检查自动跑在CI里那么这段代码的质量已经脱胎换骨。代码质量最终体现在修改成本上。没人能保证不写bug但优雅的结构能把bug限制在局部而不是牵一发动全身。这五个技巧看似平常却是从“写着跑”走向“看着放心”的分水岭。下一次你写下函数时不妨问自己六个月后的陌生人能轻松改这行代码吗能说明你此刻写得很对。