Python:求求按規范寫我
一、寫在前面
代碼被閱讀的次數遠多于編寫的次數。
我們可能花費很多時間來編寫一段代碼,一旦完成后大概率就再不會重新寫它。當這段代碼不僅是自己用時,就得注意了,每次自己或其他人瀏覽,需要快速知道它的作用及編寫它的原因,因此可讀性顯得很重要,比如:
>>> a = "Cai Xukun"
>>> b, c = a.split()
>>> print(b, c, sep=', ')
看到這種腦闊痛。那么,每個人都有自己的編碼風格,如何使整個團隊趨于一致呢?Python PEP 8早已考慮到這種情況:https://peps.python.org/pep-0008/。
我們可能互相不清楚每個人的風格,但又必須閱讀與理解各自產出的代碼,那么此時遵循PEP 8會是優秀選擇,也是每個Python從業者需要學習掌握的規范,一起看看吧!
二 、命名規范
編寫代碼時需要命名很多東西,如變量、函數、類等。選擇合理的名稱將節省以后的時間和精力,確保能夠從名稱中得到某個變量或函數代表的意義,不恰當的名稱還會徒增調試難度。
Tip:切勿使用 l 、 O 或 I 單字母名稱,因為這些名稱可能會被誤認為 1 和 0。
1.命名風格
以下是一些常見的命名約定以及如何使用示例,除了在代碼中選擇正確的命名樣式之外,還必須仔細選擇名稱:
類型 | 命名約定 | 例子 |
Variable | 使用小寫的單個字母、單詞或單詞,用下劃線分隔單詞 | x, var, my_variable |
Function | 使用一個或多個小寫單詞,用下劃線分隔單詞 | function, my_function |
Class | 每個單詞都以大寫字母開頭,這種風格稱為駝峰式命名法或帕斯卡式命名法 | Model, MyClass |
Method | 使用一個或多個小寫單詞,用下劃線分隔單詞 | class_method, method |
Constant | 使用大寫的單個字母、單詞或單詞,用下劃線分隔單詞 | CONSTANT, MY_CONSTANT, MY_LONG_CONSTANT |
Module | 使用簡短的小寫單詞,用下劃線分隔單詞 | module.py, my_module.py |
Package | 使用簡短的小寫單詞,不要用下劃線分隔單詞 | package, mypackage |
2.如何選擇名字
在編寫代碼時對命名選擇需要進行足夠的思考,在Python中為對象命名的最佳方法是使用描述性的名稱。
在命名變量時,可能會選擇簡單的、單個字母的小寫名稱。比如這篇文章開頭的引例,要將一個人的姓名存儲為字符串,并且想要使用字符串切片來以不同的方式格式化姓名嗎,寫成如下形式是不是更舒服?
>>> name = 'Cai Xukun'
>>> first_name, last_name = name.split()
>>> print(last_name, first_name, sep=', ')
同樣地,為了偷懶,你可能在選擇名稱時使用縮寫。如下示例中,定義了一個名為db()的函數,它接受一個參數x并將其乘2倍,乍一看,這似乎是一個明智的選擇,db()是double的縮寫,但在一段時間后再回到這段代碼,可能已經忘記了這個函數實現什么,并為偷懶付出相應代價:
def db(x):
return x * 2
以下寫法則要清晰得多:
def multiply_by_two(x):
return x * 2
相同的原則也適用于Python中所有其他數據類型和對象,始終盡量使用最簡潔且具有描述性的名稱總不會出錯。
三、代碼排版布局
垂直空白,即空行,對于提升代碼的可讀性起著關鍵作用。密集堆疊的代碼可能令人感到壓抑,不易理解,同樣地,代碼中過多的空行則會讓其看起來過于稀疏,帶來不必要的滾動。
將頂層函數和類用兩個空行隔開。頂層函數和類通常相對獨立,承擔不同的功能。因此,為其周圍添加適量的垂直空間是合理的,這有助于清晰地標示它們的獨立性:
class MyFirstClass:
pass
class MySecondClass:
pass
def top_level_function():
return None
用一個空行分隔類內方法定義:
class MyClass:
def first_method(self):
return None
def second_method(self):
return None
在函數內部使用空行以展示清晰的步驟。有時,一個復雜的函數在return之前需要完成多個步驟。為了幫助讀者理解函數內部的邏輯,每個步驟之間留出一個空行會很有幫助。
在下面的示例中,有一個計算列表方差的函數。這是一個兩步問題,通過在它們之間留出空行來表示每個步驟。在return之前也有一個空行,有助于清楚地看到返回了什么內容:
def calculate_variance(number_list):
sum_list = 0
for number in number_list:
sum_list = sum_list + number
mean = sum_list / len(number_list)
sum_squares = 0
for number in number_list:
sum_squares = sum_squares + number**2
mean_squares = sum_squares / len(number_list)
return mean_squares - mean**2
1.最大行長度和換行
PEP 8建議將行限制在79個字符以內,但很多時候將語句限制在79個字符或更少并不總是可能的。PEP 8概述了語句跨越多行的方法。
如果代碼包含在括號、方括號或花括號內,Python會假定它是行的延續:
def function(arg_one, arg_two,
arg_three, arg_four):
return arg_one
還可以使用反斜杠來換行:
from mypkg import example1, \
example2, example3
如果需要在二元操作符(如+和)周圍進行換行,應該在操作符之前進行*。這個規則源自數學,數學家們一致認為,在二元操作符之前換行可以提高可讀性,可以立即看到正在相加或相減的變量,因為操作符緊鄰正在操作的變量:
total = (first_variable
+ second_variable
- third_variable)
現在,讓我們看一個在二元操作符之后換行的示例:
total = (first_variable +
second_variable -
third_variable)
在這里,很難看出正在相加的變量和正在相減的變量。
在二元操作符之前換行會產生更可讀的代碼,因此PEP 8鼓勵這樣做。
2.縮進
縮進在Python中非常重要。在Python中,代碼行的縮進級別決定了語句如何分組在一起。
考慮以下示例:
x = 3
if x > 5:
print('x is larger than 5')
縮進的打印語句告訴Python只有在if語句返回True時才執行它。相同的縮進規則適用于在調用函數時告訴Python要執行哪些代碼,或者哪些代碼屬于給定的類。
PEP 8規定的關鍵縮進規則如下:
- 使用4個連續的空格來表示縮進
- 優先選擇空格而不是制表符
3.制表符 vs. 空格
如上所述,在縮進代碼時應該使用空格而不是制表符。當按下Tab鍵時,可以調整文本編輯器的設置,將制表符字符輸出為4個空格。
Python 3不允許混合使用制表符和空格。如果正在使用Python 3,則會拋出這些錯誤:
$ python3 code.py
File "code.py", line 3
print(i, j)
^
TabError: inconsistent use of tabs and spaces in indentation
可以使用制表符或空格來指示縮進的Python代碼。但是,如果使用的是Python 3,必須在選擇上保持一致,否則,代碼將無法運行。
4.換行后的縮進
當使用行延續將行保持在79個字符以下時,使用縮進來提高可讀性非常有用。幫助區分兩行代碼和跨越兩行的單行代碼??梢允褂脙煞N縮進樣式,第一種是將縮進塊與起始分隔符對齊:
def function(arg_one, arg_two,
arg_three, arg_four):
return arg_one
有時,只需要4個空格與起始分隔符對齊。這通常發生在跨多行的if語句中,因為if、空格和起始括號共計4個字符。在這種情況下,很難確定if語句內部的嵌套代碼塊從哪里開始:
x = 5
if (x > 3 and
x < 10):
print(x)
在這種情況下,PEP 8提供了兩種改進可讀性的替代方法:
- 在最終條件之后添加注釋。由于大多數編輯器中的語法高亮,這將把條件與嵌套代碼分開
x = 5
if (x > 3 and
x < 10):
# Both conditions satisfied
print(x)
- 在行延續上添加額外的縮進
x = 5
if (x > 3 and
x < 10):
print(x)
換行后的另一種縮進樣式是懸掛縮進,懸掛縮進即段落或語句中除了第一行外的每一行都縮進。可以使用懸掛縮進來在視覺上表示代碼行的延續:
var = function(
arg_one, arg_two,
arg_three, arg_four)
當使用懸掛縮進時,第一行不得有任何參數。
使用懸掛縮進時,添加額外的縮進以區分連續的行與函數內部的代碼。如以下示例很難閱讀,因為函數內部代碼與連續的行處于相同的縮進級別:
def function(
arg_one, arg_two,
arg_three, arg_four):
return arg_one
相反,最好在行延續上使用雙重縮進。這有助于區分函數參數和函數體,提高可讀性:
def function(
arg_one, arg_two,
arg_three, arg_four):
return arg_one
5.右括號怎么處理
行延續允許在括號、方括號或大括號內換行。但很容易忽略閉合括號的位置,將它放在合適的地方很重要。PEP 8提供了兩個選項來確定隱式行延續中閉合括號的位置。
將閉合括號與前一行的第一個非空白字符對齊:
list_of_numbers = [
1, 2, 3,
4, 5, 6,
7, 8, 9
]
將閉合括號與開始構造的行的第一個字符對齊:
list_of_numbers = [
1, 2, 3,
4, 5, 6,
7, 8, 9
]
可以自由選擇使用哪個選項,但請保持一致。
四、注釋
使用注釋來記錄代碼的編寫以便自己和任何合作者都能理解。在添加注釋到代碼時,請記住以下要點:
- 將注釋和文檔字符串的行長度限制在72個字符以內
- 使用完整的句子,以大寫字母開頭。
- 確保在更改代碼時更新注釋
1.塊注釋
塊注釋非常有用,幫助他人理解給定代碼塊的目的和功能,PEP 8為編寫塊注釋提供以下規則:
- 將塊注釋的縮進與它們描述的代碼保持一致
- 每行以#開頭,后跟一個空格
- 使用只包含一個#的行來分隔段落
以下是解釋for循環功能的塊注釋示例。為保持79字符行限制,句子被換行:
for i in range(0, 10):
# Loop over i ten times and print out the value of i, followed by a
# new line character
print(i, '\n')
有時,如果代碼技術含量高,那么在塊注釋中使用多個段落是必要的:
def quadratic(a, b, c, x):
# Calculate the solution to a quadratic equation using the quadratic
# formula.
#
# There are always two solutions to a quadratic equation, x_1 and x_2.
x_1 = (- b+(b**2-4*a*c)**(1/2)) / (2*a)
x_2 = (- b-(b**2-4*a*c)**(1/2)) / (2*a)
return x_1, x_2
如果不確定哪種注釋類型適合,通常可以選擇使用塊注釋。
2.行內注釋
行內注釋用于解釋代碼片段中的單個語句。以下是PEP 8關于行內注釋的建議:
- 適度使用行內注釋
- 將行內注釋寫在與其引用的語句同一行上
- 將行內注釋與語句之間用兩個或更多空格分隔開
- 與塊注釋一樣,以#和一個空格開頭
- 不要用它們來解釋顯而易見的事情
以下是行內注釋的示例:
x = 5 # This is an inline comment
有時,行內注釋是必要的,但可以使用更好的命名約定來替代:
x = 'John Smith' # Student Name
在這個示例中,行內注釋確實提供了額外的信息。但是,將“x”作為人名的變量名是很呆的做法,如果更改變量名稱,就不需要行內注釋:
student_name = 'John Smith'
最后,諸如以下的行內注釋是不好的習慣(脫褲子放P),因為陳述了顯而易見的事實并且會使代碼混亂:
empty_list = [] # Initialize empty list
x = 5
x = x * 5 # Multiply x by 5
3.文檔字符串
文檔字符串,docstrings,是出現在任何函數、類、方法或模塊的第一行的雙引號(“”")或單引號(‘’')括起來的字符串。用于解釋和記錄特定的代碼塊。PEP 257有專門闡述docstrings,docstrings最重要規則如下:
- 在docstrings兩側使用三個雙引號,如 “”“這是一個docstring”“”
- 為所有公共模塊、函數、類和方法編寫docstrings
- 將終止多行docstring的"""單獨放在一行上
def quadratic(a, b, c, x):
"""Solve quadratic equation via the quadratic formula.
A quadratic equation has the following form:
ax**2 + bx + c = 0
There always two solutions to a quadratic equation: x_1 & x_2.
"""
x_1 = (- b+(b**2-4*a*c)**(1/2)) / (2*a)
x_2 = (- b-(b**2-4*a*c)**(1/2)) / (2*a)
return x_1, x_2
- 對于單行docstrings,請將"""保持在同一行上
def quadratic(a, b, c, x):
"""Use the quadratic formula"""
x_1 = (- b+(b**2-4*a*c)**(1/2)) / (2*a)
x_2 = (- b-(b**2-4*a*c)**(1/2)) / (2*a)
return x_1, x_2
五、表達式中的空格
在表達式和語句中適當使用空格可以非常有助于閱讀。如果沒有足夠的空格,代碼可能會難以閱讀,因為它們都擠在一起。如果空格過多,則在語句中可能很難在視覺上組合相關術語。
1.二元操作符周圍的空格
在以下二元操作符的兩側都加上單個空格:
- 賦值操作符(=,+=,-=等)
- 比較(==,!=,>,<,>=,<=)和(is,is not,in,not in)
- 布爾運算(and,not,or)
Tip:當使用 = 為函數參數分配默認值時,不要用空格如:def function(default_parameter=5):
當語句中有多個操作符時,在每個操作符前后都添加單個空格可能會顯得混亂。相反,最好只在具有最低優先級的操作符周圍添加空格,特別是在進行數學操作時。以下是幾個示例:
# Recommended
y = x**2 + 5
z = (x+y) * (x-y)
# Not Recommended
y = x ** 2 + 5
z = (x + y) * (x - y)
還可以將此應用于具有多個條件的if語句:
# Not recommended
if x > 5 and x % 2 == 0:
print('x is larger than 5 and divisible by 2!')
在上面的示例中,and操作符具有最低優先級。因此,以下方式可能更清晰地表示:
# Recommended
if x>5 and x%2==0:
print('x is larger than 5 and divisible by 2!')
可以自由選擇哪種更清晰,但請注意,操作符兩側必須使用相同數量的空格。
在切片中,冒號充當二元操作符。因此,適用于前一節中概述的規則,兩側應具有相同數量的空格:
list[3:4]
# Treat the colon as the operator with lowest priority
list[x+1 : x+2]
# In an extended slice, both colons must be
# surrounded by the same amount of whitespace
list[3:4:5]
list[x+1 : x+2 : x+3]
# The space is omitted if a slice parameter is omitted
list[x+1 : x+2 :]
2.何時避免添加空格
在某些情況下,添加空格可能會使代碼更難閱讀。PEP 8明確提供了一些不適合使用空格的示例。
最容易出Bug的是在行末,即尾隨空格。因為它是不可見的,可能會產生難以追蹤的錯誤,以下概述了一些應避免添加空格的情況:
- 緊跟在括號、方括號或花括號內:
# Recommended
my_list = [1, 2, 3]
# Not recommended
my_list = [ 1, 2, 3, ]
- 在逗號、分號或冒號之前:
# Recommended
print(x, y)
# Not recommended
print(x , y)
- 在函數參數列表左括號之前:
def double(x):
return x * 2
# Recommended
double(3)
# Not recommended
double (3)
- 在索引或切片的左括號之前:
# Recommended
list[3]
# Not recommended
list [3]
- 在尾隨逗號和閉合括號之間:
# Recommended
tuple = (1,)
# Not recommended
tuple = (1, )
- 用于對齊賦值操作符:
# Recommended
var1 = 5
var2 = 6
some_long_var = 7
# Not recommended
var1 = 5
var2 = 6
some_long_var = 7
六、編程建議
1.不要使用等價運算符將布爾值與True|False進行比較
# Not recommended
my_bool = 6 > 5
if my_bool == True:
return '6 is bigger than 5'
在這里使用等價運算符(==)是不必要的,bool只能取True或False的值,以下寫法已足夠,PEP 8鼓勵使用它:
# Recommended
if my_bool:
return '6 is bigger than 5'
如果要檢查列表是否為空,首先會想要檢查列表的長度,如果列表為空,它的長度為0,在if語句中使用時等效于False:
# Not recommended
my_list = []
if not len(my_list):
print('List is empty!')
然而,在Python中,任何空列表、字符串或元組都是False。因此,可以找到一個更簡單的替代方案:
# Recommended
my_list = []
if not my_list:
print('List is empty!')
雖然這兩個示例都會輸出"List is empty!",PEP 8鼓勵使用第二個方式。
2.在if語句中,使用"is not"而不是"not … is"
# Recommended
if x is not None:
return 'x exists!'
不要寫成以下形式:
# Not recommended
if not x is None:
return 'x exists!'
在檢查字符串是否以特定詞開頭或結尾時,使用.startswith()和.endswith()而不是切片:
# Not recommended
if word[:3] == 'cat':
print('The word starts with "cat"')
使用.startswith():
# Recommended
if word.startswith('cat'):
print('The word starts with "cat"')
同樣的原則也適用于檢查后綴:
# Recommended
if file_name.endswith('jpg'):
print('The file is a JPEG')
以上就是本期的全部內容,如果想了解更多關于PEP 8的細節可以訪問:https://pep8.org/。
別忘了點贊再看哈,我是啥都生,下期再見!