Skip to main content

代码可读性

原则

函数

  • 规范的命名

    # bad
    def getknn(from_df):

    #good
    def k_nearest_neighbors(dataframe):
    • “get”这个词是无关紧要的。对于大多数命名比较好的函数来说,很明显有一些东西会从函数中返回,它的名字将反映这一点。
    • from_df 也不是必要的。如果没有明确的参数名称,函数的文档字符串或类型注释会描述参数的类型。
  • 单一功能

    def calculate_and print_stats(list_of_numbers):
    # 计算
    sum = sum(list_of_numbers)
    mean = statistics.mean(list_of_numbers)
    median = statistics.median(list_of_numbers)
    mode = statistics.mode(list_of_numbers)

    # 打印
    print('-----------------Stats-----------------')
    print('SUM: {}'.format(sum) print('MEAN: {}'.format(mean)
    print('MEDIAN: {}'.format(median)
    print('MODE: {}'.format(mode)

    很明显这个函数兼顾了两个不同类型的功能,这里就应该将这个函数进行一个按照功能类型的分构,写成两个独立的函数。

  • 每个函数都需要有一个文档字符串

  • 使用适当的语法和标点符号;用完整的句子写

  • 首先对函数的作用进行一句话的总结

  • 使用说明性语言而不是描述性语言

  • 函数的长度不应太长,行数控制在150行以内最好

高可读个案例

代码行数越少,并不意味着越好,代码的质量应该以可读性来衡量

变量交换

tmp = a
a = b
b = tmp
a,b = b,a

列表推导

my_list = []
for i in range(10):
my_list.append(i*2)
my_list = [i*2 for i in range(10)]

优化单行表达式

print('hello'); print('world')

if x == 1: print('hello,world')

if <complex comparison> and <other complex comparison>:
# do something
print'hello'
print'world'

if x == 1:
print('hello,world')

cond1 = <complex comparison>
cond2 = <other complex comparison>
if cond1 and cond2:
# do something

带索引遍历

  • 低效的做法
for i in range(len(my_list)):
print(i, "-->", my_list[i])
  • 高效的做法
l = len(my_list)
for i in range(l):
print(i, "-->", my_list[i])
  • 注重可读性的做法
for i,item in enumerate(my_list):
print(i, "-->",item)

如果需要对大量数据进行操作(10万,百万级别)建议采用高效的方式,如果是普通业务遍历,建议采用注重可读性的方式

序列解包

a, *rest = [1, 2, 3]

# a = 1, rest = [2, 3]a, *middle, c = [1, 2, 3, 4]
# a = 1, middle = [2, 3], c = 4

字符串拼接

letters = ['s', 'p', 'a', 'm']

word = ''.join(letters)

真假判断

除了使用了TrueFalseNone判断布尔值,还可以使用

# False
if "":
if []:
if {}:

# True
if not "":
if not []:
if not {}:

访问字典元素

当直接使用 [] 来访问字典里的元素时,若key不存在,是会抛异常的,所以新会可能会先判断一下是否有这个 key,有再取之。

tar = {'hello': 'world'}
key = "hello"

if tar.has_key(key):
print(d[key])
else:
print('default_value')

if key in tar:
print(d[key])
else:
print('default_value')
  • 优化
tar = {'hello': 'world'}
key = "hello"

print(d.get(key, 'default_value'))

操作列表

下面这段代码,会根据条件过滤过列表中的元素

a = [3, 4, 5]
b = []
for i in a:
if i > 4:
b.append(i)

以使用列表推导或者高阶函数 filter 来实现

a = [3, 4, 5]
b = [i for i in a if i > 4]
# Or:
b = filter(lambda x: x > 4, a)

除了 filter 之外,还有 map、reduce 这两个函数也很好用

a = [3, 4, 5]
b = map(lambda i: i + 3, a)
# b: [6,7,8]

文件读取

文件读取是非常常用的操作,在使用完句柄后,是需要手动调用 close 函数来关闭句柄的

fp = open('file.txt')
print(fp.read())
fp.close()

如果代码写得太长,即使你知道需要手动关闭句柄,却也会经常会漏掉。因此推荐养成习惯使用 with open 来读写文件,上下文管理器会自动执行关闭句柄的操作

with open('file.txt') as fp:    
for line in fp.readlines():
print(line)

代码续行

将一个长度较长的字符串放在一行中,是很影响代码可读性的(下面代码可向左滑动)

long_string = 'For a long time I used to go to bed early. Sometimes, when I had put out my candle, my eyes would close so quickly that I had not even time to say “I’m going to sleep.”'

稍等注重代码可读性的人,会使用三个引号 \来续写

long_string = 'For a long time I used to go to bed early. ' \
'Sometimes, when I had put out my candle, ' \
'my eyes would close so quickly that I had not even time to say “I’m going to sleep.”'

使用括号包裹 ()

long_string = (    
"For a long time I used to go to bed early. Sometimes, "
"when I had put out my candle, my eyes would close so quickly "
"that I had not even time to say “I’m going to sleep.”"
)

导包的时候亦是如此

from some.deep.module.inside.a.module import (
a_nice_function, another_nice_function, yet_another_nice_function
)

使用占位符

对于暂不需要,却又不得不接收的的变量,请使用占位符

filename = 'foobar.txt'

basename, _, ext = filename.rpartition('.')

_,ext = path.splitext(filename)

链式比较

score = 85
if score > 80 and score < 90:
print("良好")
  • 更好的写法
score = 85
if 80 < score < 90:
print("良好")

三目运算

对于简单的判断并赋值

age = 20
if age > 18:
type = "adult"
else:
type = "teenager"

其实是可以使用三目运算,一行搞定。

age = 20  
b = "adult" if age > 18 else "teenager"