代码规范

概述

他人阅读和理解程序的容易程度(在软件工程中通常称为"可读性")是程序最重要的质量之一。可读性好的程序会被他人使用和扩展,有时甚至持续数十年。关于这个问题,《计算机程序的构造和解释》的作者 Hal Abelsen 和 Jerry Sussman 写道:"程序必须写给人看,顺便让机器执行。"

优秀的代码规范并不意味着严格遵循规定的样式约定。编程有很多好的方式,就像有很多有效的沟通风格一样。然而,以下指导原则普遍能带来更好的代码规范:

  • 命名。对计算机来说,名称是任意符号:"xegyawebpi" 和 "foo" 与 "tally" 和 "denominator" 一样有意义。对人来说,易于理解的名称极大地有助于理解程序。为你的函数和值选择能够表明其用途、目的和含义的名称。更多建议请参见讲义中关于选择名称的部分。
  • 函数。函数是我们进行抽象的主要机制,因此每个函数理想情况下应该有一个可以在整个程序中使用的单一职责。当可以选择调用函数还是复制粘贴其主体时,努力调用函数并在程序中保持抽象。更多建议请参见讲义中关于组合函数的部分。
  • 目的。程序中的每一行代码都应该有目的。如果语句不再有任何效果(也许是因为它们在程序的先前版本中有用,但不再需要),应该将其删除。大块未使用的代码,即使变成注释,也会让读者感到困惑。可以将旧的实现保存在单独的文件中供自己使用,但不要将它们作为成品提交。
  • 简洁。用四行代码表达的想法通常比用四十行表达的相同想法更清晰。你不需要试图最小化程序的长度,但应该寻找机会通过重用已经定义的函数来大幅减少程序的大小。

命名与变量

变量和函数名称应该自描述

好的

goal, score, opp_score = 100, 0, 0
greeting = 'hello world'
is_even = lambda x: x % 2

不好的

a, b, m = 100, 0, 0
thing = 'hello world'
stuff = lambda x: x % 2

索引与数学符号

对于索引、数学符号,或者变量指代的内容很明显的情况,使用单字母名称和缩写是可以的。

好的

i = 0         # 循环计数器
x, y = 0, 0   # x 和 y 坐标
p, q = 5, 17  # 问题上下文中的数学名称

通常,ijk 是最常用的索引。

'o' 和 'l'

不要单独使用字母 'o' 和 'l' 作为名称:

不好的

o = O + 4     # 字母 'O' 还是数字 0?
l = l + 5     # 字母 'l' 还是数字 1?

不必要的变量

不要创建不必要的变量。例如:

好的

return answer(argument)

不好的

result = answer(argument)
return result

但是,如果不清楚代码指的是什么,或者表达式太长,你应该创建一个变量:

好的

divisible_by_49 = lambda x: x % 49 == 0
score = (total + 1) // 7
do_something(divisible_by_49, score)

不好的

do_something(lambda x: x % 49 == 0, (total + 1) // 7)

亵渎性语言

不要在代码中留下亵渎性语言。即使你非常沮丧。

不好的

eff_this_class = 666

命名约定

对变量和函数使用小写字母和下划线

好的

total_score = 0
final_score = 1

def mean_strategy(score, opp):
    ...

不好的

TotalScore = 0
finalScore = 1

def Mean_Strategy(score, opp):
    ...

另一方面,对类使用驼峰命名法

好的

class ExampleClass:
    ...

不好的

class example_class:
    ...

间距与缩进

空白样式看起来可能多余,但在某些地方使用空白(而在其他地方省略)通常会使代码更容易阅读。此外,由于 Python 代码依赖于空白(例如缩进),它需要一些额外的注意。

空格 vs. 制表符

使用空格,而不是制表符进行缩进。我们的起始代码总是使用 4 个空格而不是制表符。如果同时使用空格和制表符,Python 将引发IndentationError

许多文本编辑器,包括 VS Code 和 Atom,提供自动使用空格而不是制表符的设置。

缩进大小

使用 4 个空格表示缩进。从技术上讲,Python 允许你使用任意数量的空格,只要你在同一缩进级别上保持一致。常规样式是使用 4 个空格。

行长度

保持行长度在 80 个字符以内。其他约定使用 70 或 72 个字符,但 80 通常是上限。80 个字符不是硬性限制,但要做出正确的判断!长行可能表明逻辑太多,不适合放在一行上!

双行间距

不要对代码使用双行间距。也就是说,不要在代码行之间插入空行。这会增加需要的滚动量,并且违背了我们提供的其余代码的样式。

此规则的一个例外是,两个函数或类之间应该留有空格。

运算符周围的空格

+- 之间使用空格。根据表达式的清晰度,你可以自己对 */** 做出判断(只要一眼就能轻松阅读,就可以)。

好的

x = a + b*c*(a**2) / c - 4

不好的

x=a+b*c*(a**2)/c-4

列表的间距

使用元组、列表或函数操作数时,在每个逗号 , 后留一个空格:

好的

tup = (x, x/2, x/3, x/4)

不好的

tup = (x,x/2,x/3,x/4)

换行

如果一行太长,使用括号继续到下一行:

好的

def func(a, b, c, d, e, f,
         g, h, i):
    # 函数体

tup = (1, 2, 3, 4, 5,
       6, 7, 8)
names = ('alice',
         'bob',
         'eve')

注意,后续行与序列的开头对齐。添加缩进以暗示表达式继续也是很好的做法;使用最能清晰表达行继续的格式。

好的

total = (this_is(a, very, lengthy) + line + of_code
            + so_it - should(be, separated)
            + onto(multiple, lines))

空行

在函数或类的结尾和下一行之间留一个空行:

好的

def example():
    return 'stuff'

x = example() # 注意上面的空格

行尾空白

不要在行尾留下空白。

控制结构

布尔比较

不要将布尔变量与 TrueFalse 进行比较:

不好的

if pred == True:   # 不好!
    ...
if pred == False:  # 不好!
    ...

相反,这样做:

好的

if pred:           # 好!
    ...
if not pred:       # 好!
    ...

尽可能使用"隐式" False 值。示例包括空容器,如 [](){}set()

好的

if s:         # 如果 s 不为空
    ...
if not tup:   # 如果 tup 为空
    ...

检查 None

None 使用 isis not,而不是 ==!=

冗余的 if/else

不要这样做:

不好的

if pred:            # 不好!
    return True
else:
    return False

相反,这样做:

好的

return pred         # 好!

同样地:

不好的

if num != 49:
    total += example(4, 5, True)
else:
    total += example(4, 5, False)

在上面的示例中,条件之间唯一变化的是末尾的布尔值。相反,这样做:

好的

total += example(4, 5, num != 49)

此外,不要在条件的 ifelse 子句中包含相同的代码:

不好的

if pred:            # 不好!
    print('stuff')
    x += 1
    return x
else:
    x += 1
    return x

相反,将行从条件中拉出来:

好的

if pred:            # 好!
    print('stuff')
x += 1
return x

while vs. if

当你应该使用 if 时不要使用 while 循环:

不好的

while pred:
    x += 1
    return x

相反,使用 if

好的

if pred:
    x += 1
    return x

括号

不要在计算条件语句时使用括号:

不好的

if (x == 4):
    ...
elif (x == 5):
    ...
while (x < 10):
    ...

括号在 Python 条件语句中不是必需的(但在其他语言中是必需的)。

注释

回想一下,Python 注释以 # 符号开头。请记住,三重引号在技术上是字符串,而不是注释。注释可以帮助解释模糊的代码,但有一些使用它们的指导原则。

文档字符串

只在函数顶部放置文档字符串。文档字符串由函数或类开头的三重引号表示:

好的

def average(fn, samples):
    """调用一个 0 参数函数 SAMPLES 次,并取
    结果的平均值。
    """

你不应该在函数中间放置文档字符串——只放在开头。

删除注释掉的代码

从最终版本中删除注释掉的代码。你可以在调试时注释掉行,但要确保你的最终提交没有注释掉的代码。这使读者更容易识别相关的代码部分。

不必要的注释

不要写不必要的注释。例如,以下是不好的:

不好的

def example(y):
    x += 1            # 将 x 增加 1
    return square(x)  # 返回 x 的平方

你的实际代码应该是自文档化的——尽量使你在做什么尽可能明显,而不求助于注释。只有在某些内容不明显或需要明确强调时才使用注释。

重复

通常,不要重复自己(DRY)。它浪费空间,并且可能在计算上效率低下。它还可能使代码不易阅读。

不要重复复杂的表达式:

不好的

if a + b - 3 * h / 2 % 47 == 4:
    total += a + b - 3 * h / 2 % 47
    return total

相反,将表达式存储在变量中:

好的

turn_score = a + b - 3 * h / 2 % 47
if turn_score == 4:
    total += turn_score
    return total

也不要重复计算量大的函数调用:

不好的

if takes_one_minute_to_run(x) != ():
    first = takes_one_minute_to_run(x)[0]
    second = takes_one_minute_to_run(x)[1]
    third = takes_one_minute_to_run(x)[2]

相反,将表达式存储在变量中:

好的

result = takes_one_minute_to_run(x)
if result != ():
    first = result[0]
    second = result[1]
    third = result[2]

分号

不要使用分号。Python 语句不需要以分号结尾。