在python
中,有时候可能会嵌套地调用try:except
, 比如这样:
from base64 import b64decode
try:
base64Decode = b64decode(str)
except:
try:
base64Decode = b64decode(str + '=')
except:
base64Decode = b64decode(str + '==')
这段代码是我在解析学校锐捷api的时候写的,因为api返回的是没有末尾等号的Base64编码,而python
的base64
库并不支持这样的字符串的直接解析。这样写也可以用,但是不那么pythonic,谷歌了一下,有这种写法:
from base64 import b64decode
for s in (str, str+'=', str+'=='):
# 避免try的嵌套
try:
base64Decode = b64decode(s)
except:
continue
这种写法写的很舒服,也很清晰。写代码的时候一定要注意,尽量使代码扁平化,使用尽量少的嵌套,嵌套地过深就成了“嵌套地狱”,写起来不容易,阅读起来不容易,维护时更是令人恼火。异步编程中,回调地狱也是一种嵌套地狱,相比于一般的if的嵌套地狱,回调函数的嵌套更加令人抓狂,而且程序的执行顺序还非常的违反直觉,Promise
、async
等等语法形式在某种程度上都是为了解决这个问题。
八月六日整理添加
整理博客的时候看到了这一篇,之前可真的菜的真实。
这篇文章的几个问题:
1. Base64
字符串末尾的等号
Base64
编码的原理是把三个八位的字节(3x8=24)当做四个六位的部分(4x6=24),给每一部分前面补上两个0,变成了4个八位字节。六位能表示的字符数量比较少,可以全部映射到可打印的字符上,通过Base64
提供的一张表来完成这个映射。如果原始的数据的长度不能被3整除,最后会剩下不足三个字节,则应该用0补充,最终的编码输出用等号=
。
所以,最终的Base64
字符串长度一定是可以被4整除的,我们只需要补上相应数量的等号就行了。
padlen = 3 - (len(s) + 3) % 4
s += '=' * padlen
try:
b64decode(str)
except ...:
do_something()
这里用到一个技巧。如果按照一般的直觉来写的话,padlen
的求法或许是4 - len(s) % 4
,但如果s
的长度本来就是4的倍数,这样的算法就会得到4,实际上应该是0;所以作为补充,可以改为(4 - len(s) % 4) % 4
,再求一次模;但其实3 - (len(s) + 3) % 4
这种神奇的求法也可以达到同样的效果,只是不知道如何推导这两者的等价关系。
其实还是推荐用第二种比较好理解的办法。
2. for
循环避免try:except
嵌套的通用方法
之前的文章中只提到了可以避免文章中所述类型的嵌套,其实我们可以将这种方法进行推广。
比如有如下代码:
try:
method_a()
except Some:
try:
method_b()
except Some:
try:
method_c()
except Some:
do_something()
我们可以用下面这种形式替代:
for m in (method_a, method_b, method_c):
try:
m()
break
except Some:
do_something()
这样,我们就可以优雅的避免掉任何情况下的嵌套了。
这个推广参考了Python使用for/else...。但是注意一下原文第二种方式中try
语句的else
,当三个方法中的一个成功后,我们就不需要继续循环了,所以作者加上else
。而在我的实现里面,把break
放到了try
块中m()
的后面,也能达到同样的效果,而且看起来更加的清晰,try
里面的else
还是挺令人疑惑的。
3. 不应该使用str
作为变量名
str
是python的关键字。虽然在这里不会影响运行,但是不推荐。