Effective Python:编写高质量Python代码的90个有效方法(原书第2版)
上QQ阅读APP看书,第一时间看更新

第5条 用辅助函数取代复杂的表达式

Python的语法相当简明,所以有时只用一条表达式就能实现许多逻辑。例如,要把URL之中的查询字符串拆分成键值对,那么只需要使用parse_qs函数就可以了。下面的例子会解析查询字符串之中的每个参数,并把这些参数跟它们所对应的整数值放到一份字典(dict)里面。

037-01

在解析查询字符串时,可以发现,有的参数可能带有多个值,有的参数可能只有一个值,还有的参数可能是空白值,另外也会遇到根本没提供这个参数的情况。下面这三行代码分别通过get方法查询结果字典里面的三个参数,这刚好对应三种不同的情况:

037-02

如果能把参数缺失与参数为空白值这两种情况都默认当成0,那就更好了。但这样一个小小的逻辑,似乎不值得专门写if语句或辅助函数,所以有人会直接用Boolean表达式实现。

Boolean表达式用Python的语法写起来很简单,因为Python在对这种表达式求值的时候,会把空白字符串、空白list以及0值,全都当成False看待。所以,只需要把get方法查到的结果放在or操作符的左边,并且在右边写上0就行了。这样的话,只要左边的子表达式为False,那么整个表达式的值自然就被评估为右边那个表达式的值,也就是0。

038-01

读取red参数的那一行是没有问题的。因为这个键本身就在my_values字典里面,它对应的值是个只有一个元素的列表,这个元素就是字符串'5'。Python会把这样的字符串默认为True,所以整个表达式的值就等于or左侧那个子表达式的值,于是,red变量等于my_values.get('red', [''])[0]

读取green参数的那一行也没有问题。因为这个键在my_values字典里面也对应一个列表,这个列表同样只包含一个元素,也就是空白的字符串。Python会把这样的字符串默认当成False对待,所以green变量的值就等于or右侧那个子表达式的值,也就是0。

读取opacity参数的那一行同样成立。因为这个键根本就不在my_values字典之中,所以get方法会返回我们传给它的第二个值,也就是[''](参见第16条)。这个键对应的列表当然也只有一个元素,就是空白的字符串。于是,这就与读取green参数的情况一样了。

问题在于,表达式写成这样,看起来很别扭,而且它并没有完全实现我们想要的效果,因为我们还得保证解析出来的参数值是能够直接参与数学运算的整数。于是,需要通过内置的int()把这种表达式所解析出来的字符串转换成整数。

038-02

现在的代码比原来更难读了,因为看上去比原来还要乱。这种代码让人觉得很可怕:如果你是第一次阅读该代码,那必须得把整个表达式逐层拆分,才能明白这行代码的意思,这要花很长时间。代码当然应该写得短一些,但并不意味着非得挤成一行。

Python可以用if/else结构实现三元的条件表达式,这样写比刚才那种写法更清晰,且能保持代码简短。

038-03

这样写确实比原来好。如果逻辑不太复杂,那么用if/else条件表达式可以写出比较清晰的代码。但在我们这个例子里,这种写法还是不如完整的多行if/else结构好,虽然要多写几行,但非常容易看懂。对比实现逻辑的几种展开表达,刚才那种浓缩式的写法就显得更加复杂了。

039-01

如果要反复使用这套逻辑,那还是写成辅助函数比较好,即使像下面这个例子一样只用两三次,也还是值得这样做。

039-02

有了辅助函数,就可以通过更简单的代码来调用它了。这种写法要比使用or的复杂表达式与使用if/else的条件表达式清晰得多。

039-03

如果你发现表达式越写越复杂,那就应该考虑把它拆分成多个部分,并且把这套逻辑写到辅助函数里面。这样虽然要多编几行代码,但可以让程序更加清晰,所以总体来说还是值得的。语法简洁的Python虽然可以写出很多浓缩的句式,但应该避免让这样的写法把表达式弄得太复杂。我们要遵循DRY原则,也就是不要重复自己写过的代码(Don't Repeat Yourself)。

要点

  • Python的语法很容易把复杂的意思挤到同一行表达式里,这样写很难懂。
  • 复杂的表达式,尤其是那种需要重复使用的复杂表达式,应该写到辅助函数里面。
  • if/else结构写成的条件表达式,要比用orand写成的Boolean表达式更好懂。