在review同事的代码时,发现一些代码的坏味道,先看一段代码:
def get_merge_cell_rows_cols_num(one_table_data, df, List, flag):
'''
整合单元格所占行数,列数
:param one_table_data: 单表格
:param df: 二维数组
:param List: 位置列表
:param flag: 合并了的行列标志
:return:
'''
first_location = List[0] if List else []
cell_row = []
cell_column = []
for location in List:
if first_location[0] == location[0]:
cell_column.append(location[1])
if first_location[1] == location[1]:
cell_row.append(location[0])
for index_r, rows in enumerate(df):
for index_h, i in enumerate(rows):
if i == flag:
one_table_data[index_r][index_h]["rows"] = cell_row
one_table_data[index_r][index_h]["cols"] = cell_column
break
下面逐一总结一下其中的坏味道。
函数应该保持幂等性
函数是系统的元组件,组件要是写不好,整个系统的技术债就算是欠下了。而保持幂等性是系统可读性和可维护性的关键一环,关于幂等性,看这里,或者点击查看原文。
在代码中,传入了一个多维数据one_table_data,但是在函数里面修改了其中的值,这样整个函数就没法保持幂等性了,再调用一次的时候,参数就已经被修改了,这就产生了不可预测性。这时,外部调用了人就需要知道函数的实现逻辑,才能知道产生了什么副作用,可读性就降低了,出问题也比较难调戏。
就这个函数而言,修改一下就能保持幂等性,直接将21-23行修改一下就能完成:
return index_r, index_h, cell_row, cell_column
这时,函数也可以少传入一个参数one_table_data,参数越少,可读性往往越好。影响幂等性的情况,除了函数参数,通常还有:
不过,并不是所有函数都可以实现幂等性的,不能为了幂等性而幂等性。
变量取名的艺术
其实谈不上艺术,只是很多人在变量命名上确实很具迷惑性。
例如函数参数中的df, List, flag这三个参数太泛了,但看变量名很难看出更多的信息,必须得结合注释或者具体的实现才能理解其含义。这里还有一个大小写的问题,在python中习惯上,通常都是使用小写单词作为变量名,不应该混用命名方式。
单复数问题也是变量名中常见的,例如:cell_row和cell_column,这两个在定义上都是一个列表,命名上应该是复数的形式,如:cell_rows, cell_cols。(这里将column缩写成了col,这是常用的缩写,刚好缩写完之后和row就都是三个字符了。当然,缩写轻易不应该使用,可能会影响可读性)
还有第19行的代码:
for index_h, i in enumerate(rows)
这行代码看着会很迷惑,因为变量ijk之类的,通常是用于循环中的下标,而不是值,而且变量i是什么意思呢。
减少代码的缩进层数
代码中体现不多,不过也是可以优化的,函数中的两个循环体都是可以优化的,例如第一个循环体:
cell_row = []
cell_column = []
for location in List:
if first_location[0] == location[0]:
cell_column.append(location[1])
if first_location[1] == location[1]:
cell_row.append(location[0])
使用推导式,可以让代码很简洁:
cell_rows = [loc for loc in List if first_location[0] == loc[0]]
cell_columns = [loc for loc in List if first_location[1] == loc[1]]
对于第二个循环体:
for index_r, rows in enumerate(df):
for index_h, i in enumerate(rows):
if i == flag:
one_table_data[index_r][index_h]["rows"] = cell_row
one_table_data[index_r][index_h]["cols"] = cell_column
break
可以改为:
for index_r, rows in enumerate(df):
for index_h, i in enumerate(rows):
if i == flag:
continue
one_table_data[index_r][index_h]["rows"] = cell_row
one_table_data[index_r][index_h]["cols"] = cell_column
break
在一个函数内部,缩进嵌套太多是很影响可读性的,对于太多层次的嵌套通常可以通过拆分函数的方式来减少嵌套层数。
其他影响可读性的坏味道
影响代码可读性的点还有挺多: