在工作中,我们编写的代码尽可能地易于理解,这意味着:
- 变量名有意义且更长(而不是 a、b 和 c)
- 函数名称有意义且更长
- 大量注释和文档来解释代码
- 类型提示无处不在
- 字符串似乎更长、更冗长
- 等等等等
因此我们需要不断精进自己的编码能力,以适应更多的要求。
下面就跟大家分享一下以下堪称一绝的生产级 Python 代码风格。
1 使用括号解包元组
以下是一些常规元组的解包:
图片
在生产级代码中,我们通常不使用诸如 a 或 b
因此,我们可以使用括号来帮助元组解包,如下所示:
图片
请注意,通过这种方式,我们的元组解包可以容纳更长(且更具描述性)的变量名。
一个工作中的例子:
图片
2 多行列表推导式
正常的列表推导式如下所示:
图片
在生产代码中,我们通常不使用变量名 i
我将如何重写上述代码:
图片
一个工作中的例子:
图片
3 使用括号组合字符串
生产级字符串通常太冗长,无法放在一行中。因此我们使用括号将它们组合起来。
图片
注意:在括号内,字符串文字(使用引号)会自动相加,我们不需要使用 + 运算符来执行此操作
4 使用括号进行多行方法链接
正常方法链接:
图片
在生产级代码中,方法名称大多数时候都更长,并且我们将更多方法链接在一起。
我们使用括号将所有这些内容分成多行,而不是缩短任何方法名称或变量名称。
图片
请注意,如果我们在括号内进行方法链接,则不需要使用 \ 字符进行显式换行
5 索引嵌套字典
索引嵌套字典的正常方式:
图片
这里存在一些问题:
- 生产级代码中的字典有更多嵌套层级
- 字典键的名称更长
- 我们通常无法将整个嵌套索引代码压缩到一行中。
因此,我们将其分成多行,如下所示:
图片
如果这还不够的话,我们将索引代码分成更多行:
图片
或者如果我们仍然发现这很难阅读,我们可以这样做:
图片
6 编写可读且信息丰富的函数
通常,新手是这样编写函数的:
图片
包含此类代码的 PR 很可能会被拒绝
- 函数名称不具描述性
- 参数变量名不好
- 没有类型提示,所以我们乍一看不知道每个参数应该是什么数据类型
- 没有类型提示,所以我们也不知道函数应该返回什么
- 没有文档字符串,所以我们必须推断函数的作用
以下是我们在生产级 Python 代码中编写函数的方法
图片
- 函数名称应该具有描述性
- 参数名称应该更具描述性,而不是例如 a、b、c
- 每个参数都应该有一个类型提示
- 函数的返回类型也应该包括在内
- 详细说明函数功能、函数所接受的参数及其输出的文档字符串应以三重引号中的字符串形式包含在内。
7 尽可能减少缩进级别
这是一个 for 循环。如果条件满足,我们就执行某些操作。
图片
一些同事和高级工程师可能实际上会对这段代码有所不满——通过减少缩进级别可以写得更好。
我们重写这个代码,同时减少do_something()的缩进级别。
图片
请注意, do_something()的缩进级别已减少了 1 级,只需使用 if not condition而不是if condition。
在生产级代码中,缩进级别可能会更多,如果缩进太多,我们的代码就会变得烦人且难以阅读。因此,这个技巧可以让我们的代码更整洁、更易于阅读,
8 带括号的布尔条件
这是一个使用and关键字连接起来的包含 3 个条件的 if 语句。
图片
在生产级代码中,条件会变得更长,并且可能会有更多条件。因此,我们可以解决这个问题的一种方法是将这个巨大的条件重构为一个函数。
或者,如果我们认为没有必要为此编写新函数,则可以使用括号编写条件语句。
图片
这样,我们就不会被迫为这一个条件语句编写一个新的函数或变量,同时我们还能保持它的整洁和可读性。
有时我可能实际上更喜欢这样写,尽管这只是基于个人喜好:
图片
9 防止 None 值
访问对象某些嵌套属性的普通代码。
图片
此代码中的一些问题可能会导致我们的 PR 被拒绝:
- 如果dog为 None,我们会收到错误
- 如果dog.owner为 None,我们也会收到错误
- 本质上,这段代码不能防止dog或dog.owner为 None 的可能性。
在生产级代码中,我们需要积极防范此类情况。下面是我重写此代码的方法。
图片
Python 中的 and 和 or
- 如果 dog 为 None,我们的表达式终止于 “if dog
- 如果 dog 不为 None,但 dog.owner 为 None,则表达式终止于“ if dog and dog.owner
- 如果我们没有任何 None 值,则可以成功访问dog.owner.name,并将其与字符串“bob”进行比较
这样,我们就有了额外的保护,防止dog或dog.owner为 None 值的可能性。
10 防止迭代 None 值
以下是我们如何迭代某些可迭代对象(例如列表、字典、元组等)
图片
这样做的一个问题是,它不能防止 mylist 为 None —— 如果 mylist
以下是我对这段代码的改进:
图片
表达式 “mylist or None”
- 如果 mylist
- 如果 mylist
因此,如果 mylist 为 None,则表达式 “mylist or None”
11 内部函数以 _ 开头
这是一个示例类。在这里,run方法使用其他方法 clean 和 transform
图片
在生产级代码中,我们希望尽可能明确,因此尝试区分内部和外部方法。
- 外部方法——供其他类和对象使用的方法
- 内部方法——类本身使用的方法
按照惯例,内部方法以下划线 _ 开头是一种很好的做法
如果我们重写上面的代码,我们会得到:
图片
注意:在方法名前面添加下划线并不会将其隐藏在其他类和对象中。事实上,功能上没有区别。
12 常用功能装饰器
这是一个包含 3 个函数的类,每个函数执行不同的操作。但是,请注意,不同函数之间的步骤类似 — try-except 块以及日志记录功能。
图片
减少重复代码量的一个很好的做法是编写一个包含常用功能的装饰函数。
图片
这样,如果我们想要更新公共代码(try-except 和日志代码),我们不再需要在 3 个地方更新它们 - 我们只需要更新包含公共功能的装饰器代码。