用pythonic方式来思考(3)
第10条:尽量用 enumerate 取代 range
内置 range 函数可以完成整数上的迭代,如:
for i in range(5):
print(i)
>>>
0
1
2
3
4
然而对列表进行迭代时,还想知道索引的时候,如果用 range 函数,只能勉强这样:
list = [1, 2, 3, 4, 5]
for i in range(len(list)):
print(i, list[i])
>>>
0 1
1 2
2 3
3 4
4 5
python 提供了内置的 enumerate 函数,可以优雅的处理这种问题,
list = [1, 2, 3, 4, 5]
for index, value in enumerate(list): # enumerate 把迭代器包装为生成器
print(index, list[index])
>>>
0 1
1 2
2 3
3 4
4 5
第11条:用 zip 函数同时遍历两个迭代器
在同时遍历两个列表,而且两个列表之间存在关联的时候,就可以使用 zip 来进行迭代。zip 能够将两个和两个以上的迭代器中的值封装成生成器。
name_list = ['xiaoli', 'xiaowu', 'xiaolan', 'xiaonwang']
len_name = [len(n) for n in name_list]
max_len = 0
for name, len_name in zip(name_list, len_name):
if len_name > max_len:
max_len = len_name
longest_name = name
print(longest_name, max_len)
>>>
xiaonwang 9
在 Python2 中zip不是生成器,而是迭代器产生的值的元组构成的列表,会占用大量内存。应该使用 itertools 中的 izip 函数。
zip 函数遍历到任何一个迭代器结束时,就停止,所以如果有多个长度不同的迭代器时,在迭代次数最少的迭代器结束时就停止遍历其他的迭代器了。
第12条:不要在 for 和 while 循环后面写 else 块
Python 支持在循环内部的语句块后直接编写 else 块。
for i in range(3):
print('Loop %d' % i)
else:
print('Else block!')
>>>
Loop 0
Loop 1
Loop 2
Else block!
从上面运行实例可以看出,else 块在循环结束后直接执行。
for i in range(3):
print('Loop %d' % i)
if i == 1:
break
else:
print('Else block!')
>>>
Loop 0
Loop 1
Loop 2
这时我们在 循环语句中加上 break 提前结束循环,这时就没有执行 else 块。while 语句也是如此,
i = 0
while True:
i += 1
print('Loop %d' % i)
if i > 3:
break
else:
print('Else block!')
>>>
Loop 0
Loop 1
Loop 2
Loop 3
所以, 循环后面的 else 只会在循环完全结束时才会执行,提前 break 出来时不会执行的。
第13条:合理利用 try/except/else/finally 结构中的每个代码块
Python 程序的异常处理可能要考虑四种不同的时机,这些时机可以用 try、except、else 和 finally 来表述。
1.finally 块
既要将异常向上传播,又要在异常发生时执行清理工作,就可以使用 try/finally 结构。这种结构可以用来确保程序能够可靠地关闭文件句柄。
handle = open('/tmp/random_data.txt') #open 放在 try 外面,否则open时IOError异常不会进入finally
try:
data = handle.read()
finally:
handle.close()
2.else块
在 try/else模块中,如果 try 块没有异常,就会执行 else 块。有了这个,就应该缩减 try 内的代码量,使其更加易读。
例如在字符串中加载 json 字典数据,返回字典里某个键所对应的值:
def load_json_key(data, key):
try:
result_dict = json.loads(data)
except ValueError as e:
raise KeyError from e
else:
return result_dict[key]
这种写法使异常行为更加清晰。
3.混合使用
如果在复合语句中把上面几种机制都用到的话,可以使代码更简洁易懂。
比如在读取文件内容并对读取内容进行处理时,可以这样:
def devide_json(path):
handle = open(path, 'r+')
try:
data = handle.read()
op = json.loads(data)
value = (
op['numerator']/
op['denominator']
)
except ZeroDivisionError as e:
return UNIDEFINED
else:
op['result'] = value
result = json.dumps(op)
handle.seek(0)
handle.write(result)
return value
finally:
handle.close()