如何用PEP 8編寫優(yōu)雅的Python代碼

h_41.jpg

目錄如下:

PEP 8有時候讀作PEP8或者PEP-8,是一份提供如何編寫Python代碼指南和最佳實踐的文檔,由Guido van Rossum, Barry Warsaw, Nick Coghlan在2001年完成绪穆。PEP 8主要注重于提高 Python 代碼的可讀性和一致性重贺。

PEP全稱為:Python Enhancement Proposal,一個PEP是一份文檔恃慧,它描述了為Python提出的新特性以及為社區(qū)編寫的Python方面的文檔,比如設(shè)計和風(fēng)格。

本教程概述了PEP 8中列出的關(guān)鍵指南铅搓,它的目標(biāo)讀者是初級到中級程序員,因此我沒有涉及一些最高級的主題搀捷。不過你可以通過閱讀完整的PEP 8 — Style Guide for Python Code | Python.org文檔來學(xué)習(xí)高級主題星掰。

在本教程結(jié)束時,你將能夠:

  • 編寫遵從PEP 8規(guī)范的代碼
  • 理解PEP 8中列出的指導(dǎo)原則背后的原因
  • 設(shè)置你的開發(fā)環(huán)境嫩舟,以便開始編寫符合PEP 8標(biāo)準(zhǔn)的Python代碼

Why We Need PEP 8

可讀性很重要

PEP 8規(guī)范存在的目的在于提高Python代碼的可讀性氢烘,但為什么可讀性如此重要?為什么編寫具有可讀性的代碼是Python語言的指導(dǎo)原則之一家厌?

正如Guido van Rossum所說:”代碼閱讀的頻率遠(yuǎn)遠(yuǎn)高于編寫的頻率“播玖。你可能花費幾分鐘或一整天時間編寫一段代碼來處理用戶身份驗證,一旦你寫完它像街,你就再也不會重寫一次黎棠,但是你肯定會再讀一次晋渺,這段代碼可能仍然是你正在進(jìn)行的項目的一部分。每次你回到那個代碼文件脓斩,你必須要記住那部分代碼是做什么的以及你為什么要寫它木西,所以可讀性很重要。

如果你是Python新手随静,在你編寫代碼之后的幾天或幾周內(nèi)還要你記住代碼片段的含義可能有點困難八千。如果你遵循PEP 8標(biāo)準(zhǔn),你可以很確定你對你的變量命名得很好燎猛,你會知道你添加了足夠的空格恋捆,因此更容易遵循代碼中的邏輯步驟,你還可以對你的代碼進(jìn)行很好的注釋重绷,所有的這些都意味著你的代碼具有更高的可讀性沸停,也更容易再次閱讀。作為初學(xué)者昭卓,遵循PEP 8的規(guī)則可以使學(xué)習(xí)Python成為更愉快的任務(wù)愤钾。

如果你正在找工作,那么遵循PEP 8標(biāo)準(zhǔn)尤為重要候醒,編寫清晰可讀性高的代碼更會突出你的專業(yè)性能颁,這將會側(cè)面告訴你的老板,你了解怎么很好地構(gòu)建你的代碼倒淫。

如果你擁有很多編寫Python代碼的經(jīng)驗伙菊,然后你可能需要和其他人協(xié)作開發(fā),編寫可讀性高的代碼在這里至關(guān)重要敌土,其他人可能此前從未看過你這樣風(fēng)格的代碼镜硕,他必須重新閱讀且理解你的代碼風(fēng)格,如果擁有你遵循和認(rèn)可的指南將使其他人更容易閱讀你的代碼纯赎。

Naming Conventions

明了勝于晦澀

當(dāng)你編寫Python代碼的時候谦疾,你必須對很多東西命名:變量南蹂、函數(shù)犬金、類、包等等六剥,選擇合理的名字將為你節(jié)省時間和精力晚顷。你將能夠從名稱中找出某個變量,函數(shù)或類所代表的含義疗疟。你還要避免使用不恰當(dāng)?shù)拿Q该默,因為這樣可能會導(dǎo)致難以調(diào)試的錯誤。

::Note: 永遠(yuǎn)不要使用 i策彤,o栓袖,或 I 單字母名稱匣摘,因為這些名稱可能會被誤認(rèn)為1和0,具體取決于字體:::

# 這可能看起來像你試圖將2重新分配給零
0 = 2 

Naming Styles

下表概述了Python代碼中的一些常見命名風(fēng)格以及何時應(yīng)該使用它們:

image

這些是一些常見的命名約定以及如何使用它們的示例裹刮,但是為了編寫高可讀性代碼音榜,你仍然需要謹(jǐn)慎選擇字母和單詞。除了在代碼中選擇正確的命名風(fēng)格外捧弃,還必須仔細(xì)選擇名稱赠叼,以下是有關(guān)如何盡可能有效地執(zhí)行此操作的一些指示。

How to Choose Names

為變量违霞、函數(shù)嘴办、類等選擇名稱是一項挑戰(zhàn),在編寫代碼時买鸽,你應(yīng)該在命名選擇中加入相當(dāng)多的思考涧郊,因為這樣可以使代碼更具可讀性。在Python中為對象命名的最佳方法是使用描述性名稱來清楚表明對象所代表的內(nèi)容眼五。

對變量進(jìn)行命名時底燎,你可能會選擇簡單的單字母小寫名稱,例如x弹砚。但是双仍,除非你使用x作為數(shù)學(xué)函數(shù)的參數(shù),否則并不清楚x代表什么桌吃。想象下你正在將一個人的名字保存為字符串朱沃,并且你希望使用字符串切片來格式化其名稱。你最終會得到這樣的東西:

# Not recommended
x = 'John Smith'
y, z = x.split()
print(z, y, sep=', ')
# 'Smith, John'

上面代碼可以很好的運行茅诱,但是你必須追蹤x,y,z表示的含義逗物,這樣也會讓你的代碼協(xié)作者產(chǎn)生困擾,更明確的名稱選擇將是這樣的:

# Recommended
name = 'John Smith'
first_name, last_name = name.split()
print(last_name, first_name, sep=', ')
# 'Smith, John'

同樣瑟俭,為了減少輸入的數(shù)量翎卓,在選擇名稱時使用單詞縮寫也是很有誘惑力的,在下面的例子中摆寄,我定義了一個函數(shù)db()失暴,它接受一個參數(shù)x并將其加倍返回:

# Not recommended
def db(x):
    return x * 2

乍一看,這似乎是一個明智的選擇微饥。 db()很容易看成是double的縮寫逗扒,但想象一下,幾天后再看這段代碼欠橘,你可能已經(jīng)忘記了你試圖通過這個函數(shù)實現(xiàn)的目標(biāo)是什么矩肩,這將使猜測它原本的含義變得困難。

下面的例子就比較清晰肃续,如果你幾天后再看這段代碼黍檩,你仍然可以閱讀并理解此函數(shù)的目標(biāo):

# Recommended
def multiply_by_two(x):
    return x * 2

同樣的理念適用于Python中的所有其他數(shù)據(jù)類型和對象叉袍,盡量一直使用最簡潔且最具描述性的名稱。

Code Layout

優(yōu)美勝于丑陋

如何對代碼布局對于提升它的可讀性有很大的作用刽酱。在本節(jié)中畦韭,你將學(xué)習(xí)如何添加垂直空格以提高代碼的可讀性。你還將學(xué)習(xí)如何處理PEP 8中建議的79字符行限制肛跌。

Blank Lines

垂直空格或空行可以極大地提高代碼的可讀性艺配,聚集在一起的代碼可能是壓倒性的且難以閱讀,同樣衍慎,代碼中的空行太多會使其看起來非常稀疏转唉,讀者可能需要進(jìn)行沒必要的滾動,以下是關(guān)于如何使用垂直空白的三個關(guān)鍵指南稳捆。

用兩個空行包圍頂級函數(shù)和類赠法,頂級函數(shù)和類應(yīng)該是完全自包含的,并處理單獨的功能乔夯,在它們周圍放置額外的垂直空間是有意義的砖织,因此很明顯它們是分開的:

class MyFirstClass:
    pass


class MySecondClass:
    pass


def top_level_function():
    return None

用一個空行包圍類中的方法定義,在一個類中末荐,函數(shù)都彼此有聯(lián)系侧纯,最好只在它們之間留一行:

class MyClass:
    def first_method(self):
        return None

    def second_method(self):
        return None

在函數(shù)內(nèi)部謹(jǐn)慎地使用空行,以顯示清晰的步驟甲脏,有時候一個復(fù)雜的函數(shù)必須在return語句之前完成好幾個步驟眶熬,為了幫助閱讀者理解函數(shù)里面的邏輯,在每個步驟之間留一個空行會很有幫助块请。

在下面的示例中娜氏,有一個計算列表方差的函數(shù),這個問題可以分為兩個步驟墩新,所以我在每個步驟之間留下了一個空行贸弥,在return語句之前還有一個空行。這有助于讀者清楚地看到返回的內(nèi)容:

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

如果仔細(xì)地使用垂直空格海渊,可以大大提高代碼的可讀性绵疲,它有助于讀者直觀地理解你的代碼如何分成幾個部分,以及這些部分如何相互關(guān)聯(lián)切省。

Maximum Line Length and Line Breaking

PEP 8建議每行代碼限制在79個字符以內(nèi)最岗,這是因為它允許多個文件并排打開帕胆,同時也避免了換行朝捆,當(dāng)然,將語句保持在79個字符以內(nèi)并不總是可行的懒豹。PEP 8概述了允許語句在多行上運行的方法芙盘。

如果代碼包含在括號驯用、方括號或大括號中,Python將會認(rèn)為代碼是一行的:

def function(arg_one, arg_two,
             arg_three, arg_four):
    return arg_one

如果不能用上述的方式進(jìn)行每行代碼延續(xù)儒老,那么可以使用反斜杠代替換行:

from mypkg import example1, \
    example2, example3

但是蝴乔,如果可以使用第一個方案,那么就應(yīng)該盡量這樣做驮樊。

如果需要在二元運算符周圍做換行操作薇正,例如+*,那么需要將換行操作放在前面囚衔,這條規(guī)則源于數(shù)學(xué)挖腰,數(shù)學(xué)家同意在二元運算符之前換行以可提高可讀性,比較以下兩個例子练湿。

下面是在二元運算符之前換行的示例:

# Recommended
total = (first_variable
         + second_variable
         - third_variable)

你可以馬上反應(yīng)過來哪些變量正在相加后者相減猴仑,因為操作符緊鄰著變量。

現(xiàn)在肥哎,讓我們看一個在二元運算符之后換行的示例:

# Not Recommended
total = (first_variable +
         second_variable -
         third_variable)

在這里辽俗,很難看出哪個變量被加,哪個變量被減篡诽。

在二元運算符之前換行可以讓代碼更加具有可讀性崖飘,所PEP 8鼓勵這種方式。在二元運算符之后換行的代碼仍然符合PEP 8杈女,但是坐漏,我們鼓勵你在二元運算符之前換行。

Indentation

當(dāng)存在多種可能碧信,不要嘗試去猜測
而是盡量找一種赊琳,最好是唯一一種明顯的解決方案(如果不確定,就用窮舉法)

縮進(jìn)砰碴,即前導(dǎo)空格躏筏,在Python中非常重要。Python中代碼行的縮進(jìn)級別決定了語句如何組合在一起呈枉。

思考下面的例子:

x = 3
if x > 5:
    print('x is larger than 5')

縮進(jìn)的print語句使得Python明白只有if語句返回True才能繼續(xù)執(zhí)行它趁尼,同樣的縮進(jìn)適用于告訴 Python 在調(diào)用函數(shù)時要執(zhí)行哪些代碼,或者哪些代碼屬于給定的類猖辫。

PEP 8制定的關(guān)鍵縮進(jìn)規(guī)則如下:

  • 使用4個連續(xù)的空格來表示縮進(jìn)酥泞。
  • 選擇空格而不是制表符(Tab)。

Tabs vs. Spaces

如同上述所說啃憎,當(dāng)你進(jìn)行縮進(jìn)的時候芝囤,你最好用空格代替制表符,你可以直接對文本編輯器的設(shè)置進(jìn)行調(diào)整,當(dāng)你按Tab的時候轉(zhuǎn)換為4個空格的輸出

如果你使用的是Python 2并且混合使用了制表符和空格來縮進(jìn)代碼悯姊,當(dāng)你運行的時候可能不會看見錯誤羡藐。為了幫助你檢查一致性,可以在從命令行運行Python 2代碼時添加-t標(biāo)志悯许。當(dāng)你不一致地使用制表符和空格時仆嗦,解釋器將會發(fā)出警告:

python2 -t code.py
code.py: inconsistent use of tabs and spaces in indentation

相反,如果你使用-tt標(biāo)志先壕,解釋器將發(fā)出錯誤而不是警告瘩扼,使用此方法的好處是解釋器會告訴你縮進(jìn)不一致的位置在哪里:

python2 -tt code.py
File "code.py", line 3
    print(i, j)
             ^
TabError: inconsistent use of tabs and spaces in indentation

Python 3不允許制表符和空格混合使用,因此垃僚,如果你使用的是Python3邢隧,解釋器將會自動報錯:

python3 code.py
File "code.py", line 3
    print(i, j)
              ^
TabError: inconsistent use of tabs and spaces in indentation

你可以在編寫Python代碼中使用制表符或空格來表示縮進(jìn),但是冈在,如果你使用的是Python 3,則必須與你的選擇保持一致包券,否則你的代碼將不能運行纫谅。PEP 8推薦你總是使用4個連續(xù)的空格來表示縮進(jìn)。

Indentation Following Line Breaks

當(dāng)你為了保證每行在79個字符以下而進(jìn)行換行時溅固,使用縮進(jìn)來提高代碼可讀性是很有用的付秕,它允許讀者區(qū)分兩行代碼和跨越兩行的單行代碼,這里有兩種縮進(jìn)風(fēng)格供你選擇侍郭。

第一個是將縮進(jìn)塊與開始的分隔符對齊:

def function(arg_one, arg_two,
             arg_three, arg_four):
    return arg_one

有時你會發(fā)現(xiàn)只需要4個空格就可以與開口分隔符對齊询吴,這種情況經(jīng)常發(fā)生在 if 語句中,如果跨多行的if+ +(構(gòu)成4個字符亮元,那么就會發(fā)生這種情況猛计,在這種情況下,很難確定 if 語句中嵌套的代碼塊從哪里開始:

x = 5
if (x > 3 and
    x < 10):
    print(x)

在這種情況下爆捞,PEP 8提供了兩種替代方案來幫助你提高代碼可讀性:

  • 在最后的條件后添加注釋奉瘤,由于大多數(shù)編輯器中有語法高亮顯示,這可以將條件與嵌套代碼分開:
x = 5
if (x > 3 and
    x < 10):
    # Both conditions satisfied
    print(x)
  • 在換行中添加額外的縮進(jìn):
x = 5
if (x > 3 and
        x < 10):
    print(x)

換行符后的另一種縮進(jìn)方式是懸掛縮進(jìn)(hanging indent)煮甥,這是一個印刷術(shù)語盗温,意思是段落或語句中除第一行以外的每一行都縮進(jìn),可以使用懸掛縮進(jìn)直觀地表示一行代碼的延續(xù)成肘。下面是一個例子:

var = function(
    arg_one, arg_two,
    arg_three, arg_four)

::Note:當(dāng)你正在使用hanging indent時卖局,第一行不得有任何參數(shù)。以下示例不符合PEP 8::

# Not Recommended
var = function(arg_one, arg_two,
    arg_three, arg_four)

使用hanging indent時双霍,可以添加額外的縮進(jìn)以區(qū)分連續(xù)行與函數(shù)內(nèi)包含的代碼砚偶。下面是一個難以閱讀的例子批销,因為函數(shù)里面的代碼和連續(xù)行的縮進(jìn)等級是相同的:

# Not Recommended
def function(
    arg_one, arg_two,
    arg_three, arg_four):
    return arg_one

相反,最好在一行代碼換行后使用雙縮進(jìn)蟹演,這有助于你區(qū)分函數(shù)參數(shù)和函數(shù)內(nèi)部構(gòu)造风钻,從而提高可讀性:

def function(
        arg_one, arg_two,
        arg_three, arg_four):
    return arg_one

當(dāng)你編寫遵循PEP 8規(guī)范代碼的時候顷蟀,每行字符在79之內(nèi)的規(guī)則使你必須強制換行酒请,為了提高可讀性,你應(yīng)該縮進(jìn)一行鸣个,以表明它是一個連續(xù)的行羞反。做到這一點有兩種方法,第一種是將縮進(jìn)的塊與開始的分隔符對齊囤萤,第二種是使用懸掛縮進(jìn)昼窗,在換行之后,你可以自由選擇使用哪種方法進(jìn)行縮進(jìn)涛舍。

Where to Put the Closing Brace

一行代碼的換行操作允許你在括號澄惊、方括號或大括號后面斷開一行,這會造成一個問題富雅,就是很容易忘記關(guān)閉括號掸驱,但重要的是要把它放在合理的地方,否則没佑,它會使讀者感到困惑毕贼,Pep 8為隱含的行延續(xù)中的結(jié)束括號的位置提供了兩個選項:

  • 使用前一行的第一個非空白字符排列右括號:
list_of_numbers = [
    1, 2, 3,
    4, 5, 6,
    7, 8, 9
    ]
  • 將結(jié)束括號與聲明行的第一個字符對齊:
list_of_numbers = [
    1, 2, 3,
    4, 5, 6,
    7, 8, 9
]

你可以自由地選擇任何一種方式,但是蛤奢,和之前說的一樣鬼癣,一致性是關(guān)鍵,所以之后的代碼就要堅持使用你選擇的某一個方案以保持代碼一致性啤贩。

Comments

如果你無法向人描述你的方案待秃,那肯定不是一個好方案;反之亦然

你應(yīng)該使用注釋來說明編寫的代碼痹屹,這對記錄你的代碼非常重要锥余,這樣你的任何協(xié)作者都可以理解它。當(dāng)你或者其他人閱讀代碼注釋痢掠,他們應(yīng)該能夠很容易地理解注釋所應(yīng)用的代碼驱犹,以及它如何與代碼的其余部分相匹配。

下面是一些你為你代碼作注釋時候需要注意的關(guān)鍵點:

  • 將注釋和文檔字符串的行長限制為72個字符
  • 使用完整的句子足画,以大寫字母開頭
  • 注釋隨著代碼的更新而更新

Block Comments

使用塊注釋來記錄一小部分代碼雄驹,當(dāng)你必須編寫幾行代碼來執(zhí)行單個操作時,它們非常有用淹辞,例如從文件導(dǎo)入數(shù)據(jù)或更新數(shù)據(jù)庫記錄医舆。其重要性在于它們可以幫助其他人理解給定代碼塊的用途和功能。

PEP 8提供以下規(guī)則來編寫注釋:

  • 將塊注釋縮進(jìn)到其描述代碼的相同級別
  • 用#加上一個空格開始每一行的注釋
  • 用包含單個#的行分隔段落

這是一個解釋for循環(huán)功能的塊注釋,請注意蔬将,句子為了每行保留79個字符行限制進(jìn)行了換行:

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')

有時爷速,如果代碼技術(shù)性較強,那么有必要在塊注釋中使用多個段落:

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

如果你不確定什么樣的注釋類型是合適的霞怀,那么塊注釋通常是正確的選擇惫东。請盡量在你的代碼中添加注釋,一旦你對代碼進(jìn)行了更新也請務(wù)必對注釋進(jìn)行更新毙石!

Inline Comments

行內(nèi)注釋對單個語句中的一段代碼進(jìn)行了解釋廉沮,這有助于提醒你或者其他人為什么某行代碼是必須的,以下是 PEP 8對他們的評價:

  • 謹(jǐn)慎使用行內(nèi)注釋
  • 在與它們引用的語句相同的行上寫入行內(nèi)注釋
  • 從語句中用兩個或多個空格分隔行內(nèi)注釋
  • 像塊注釋那樣徐矩,用#加上一個空格開始每一行的注釋
  • 不要用它們來解釋明顯的問題

下面是一個行內(nèi)注釋的例子:

x = 5  # This is an inline comment

有時滞时,行內(nèi)注釋似乎是必要的,但是你可以使用更好的命名約定滤灯,下面是一個例子:

x = 'John Smith'  # Student Name

這里坪稽,行內(nèi)注釋為變量提供了額外的說明信息,然而用x作為一個人名的變量名不是很好的做法鳞骤,如果你對變量重命名窒百,是可以不用使用行內(nèi)注釋的:

student_name = 'John Smith'

最后,像這樣的行內(nèi)注釋是不好的做法弟孟,因為它們陳述了顯而易見且雜亂的代碼:

empty_list = []  # Initialize empty list

x = 5
x = x * 5  # Multiply x by 5

行內(nèi)注釋比塊注釋更具體嫌拣,并且容易在非必要時候添加它們扎酷,這會導(dǎo)致代碼混亂饱溢。除非你確定需要行內(nèi)注釋帅刊,不然使用塊注釋避免代碼混亂的問題比較好,堅持使用塊注釋陈症,可以讓你的代碼更加符合PEP8的標(biāo)準(zhǔn)蔼水。

Documentation Strings

Documentation strings 或者說docstrings, 是用雙引號(”””)或單引號(’’’)括起來的字符串录肯,它們出現(xiàn)在任何函數(shù)趴腋,類,方法或模塊的第一行论咏,你可以使用它們來解釋和記錄特定的代碼塊优炬。這里有一個對應(yīng)的PEP,見PEP 257厅贪,但你可以從本節(jié)閱讀總結(jié)的經(jīng)驗:

使用文檔字符串的重要規(guī)則如下:

  • 文檔字符串兩邊都有三個雙引號環(huán)繞蠢护,比如:"""This is a docstring"""
  • 為所有公共模塊,函數(shù)养涮,類和方法編寫docstrings
  • 使用單行"""結(jié)束多行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

想要了解更多眉抬,請閱讀Documenting Python Code: A Complete Guide – Real Python

Whitespace in Expressions and Statements

間隔勝于緊湊

正確使用空格在表達(dá)式和語句中非常有用,如果代碼中沒有足夠的空格懈凹,那么代碼應(yīng)該很難閱讀蜀变,因為他們都湊在一起,如果空格太多介评,又會難以在視覺上將完整的語句聯(lián)系出來库北。

Whitespace Around Binary Operators

在進(jìn)行以下二元操作的時候,應(yīng)該在其兩邊加上空格:

  • 分配操作(=, +=, -=, 等等)
  • 比較(==, !=, >, <. >=, <=)和 (is, is not, in, not in)
  • 布爾(and, not, or)

::Note:當(dāng)=是為一個函數(shù)的參數(shù)賦值時候就不用在兩邊加空格::

# Recommended
def function(default_parameter=5):
    # ...


# Not recommended
def function(default_parameter = 5):
    # ...

如果語句中有多個運算符威沫,則在每個運算符前后添加單個空格可能會讓人感到困惑贤惯,相反洼专,最好只在優(yōu)先級最低的運算符周圍添加空格棒掠,尤其是在執(zhí)行數(shù)學(xué)運算時。以下是幾個例子:

# Recommended
y = x**2 + 5
z = (x+y) * (x-y)

# Not Recommended
y = x ** 2 + 5
z = (x + y) * (x - y)

還可以將此應(yīng)用于有多個條件的if語句:

# Not recommended
if x > 5 and x % 2 == 0:
    print('x is larger than 5 and divisible by 2!')

在上面的示例中屁商,and操作具有最低優(yōu)先級烟很,因此,可以下面這樣更清楚地表達(dá)if語句:

# Recommended
if x>5 and x%2==0:
    print('x is larger than 5 and divisible by 2!')

你可以自由選擇哪個方式更清晰蜡镶,但需要注意的是你必須在運算符的任何一側(cè)使用相同數(shù)量的空格雾袱。

下面的例子是不被允許的:

# Definitely do not do this!
if x >5 and x% 2== 0:
    print('x is larger than 5 and divisible by 2!')

在切片中,冒號作為二元運算符官还。因此芹橡,上一節(jié)中概述的規(guī)則也適用于此,并且其任何一方都應(yīng)該有相同數(shù)量的空白望伦,以下列表切片示例是合法的:

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 :]

總之林说,你應(yīng)該使用空格包圍大多數(shù)運算符,但是屯伞,此規(guī)則有一些注意事項腿箩,例如在函數(shù)參數(shù)中或在一個語句中組合多個運算符時。

When to Avoid Adding Whitespace

在某些情況下劣摇,添加空格可能會使代碼難以閱讀珠移,太多的空格會使代碼過于稀疏而難以理解。 PEP 8概述了一些非常明顯的例子應(yīng)當(dāng)不使用空格

在一行的末尾避免添加空格是最重要的地方末融,這稱為trailing whitespace钧惧,它是不可見的,可能產(chǎn)生難以追蹤的錯誤勾习。

以下列表概述了一些應(yīng)避免添加空格的情況:

  • 直接放在括號浓瞪、方括號或大括號內(nèi):
# Recommended
my_list = [1, 2, 3]

# Not recommended
my_list = [ 1, 2, 3, ]
  • 在逗號,分號或冒號之前:
x = 5
y = 6

# Recommended
print(x, y)

# Not recommended
print(x , y)
  • 在打開函數(shù)調(diào)用的參數(shù)列表的開括號之前:
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

確保你的代碼中沒有尾隨空格语卤。 在其他情況下追逮,PEP 8不鼓勵添加額外的空格酪刀,例如立直接放在括號、方括號或大括號內(nèi)钮孵,以及逗號和冒號之前骂倘。 為了對齊操作符,也不應(yīng)該添加額外的空格巴席。

Programming Recommendations

簡潔勝于復(fù)雜

你經(jīng)常會發(fā)現(xiàn)有幾種方法可以在 Python (以及任何其他編程語言)中執(zhí)行類似的操作历涝。在本節(jié)中,你將看到PEP 8提供的一些建議用來消除這種歧義并且保持一致性漾唉。

不要使用等價運算符將布爾值與True或False進(jìn)行比較荧库,你經(jīng)常需要檢查布爾值是True還是False,當(dāng)這樣操作時赵刑,請使用如下所示的語句直觀地執(zhí)行此操作:

# Not recommended
my_bool = 6 > 5
if my_bool == True:
    return '6 is bigger than 5'

這里不需要使用等價運算符==分衫, bool只能取值True或False,按照下面這樣寫就行了:

# Recommended
if my_bool:
    return '6 is bigger than 5'

這種在if語句中使用bool的方式可以寫更少的代碼并且更加簡單般此,因此PEP 8鼓勵使用這種方式蚪战。

在if語句中判斷列表是否為空,當(dāng)你希望確定列表是否為空時铐懊,你通常會判斷列表的長度邀桑,如果列表為空,那么長度為0科乎,在if語句中使用時等于False壁畸,這里有一個例子:

# Not recommended
my_list = []
if not len(my_list):
    print('List is empty!')

但是,在Python中茅茂,任何空列表捏萍,字符串或元組都可以當(dāng)為False的。因此玉吁,我們可以提出一個更簡單的替代方案:

# Recommended
my_list = []
if not my_list:
    print('List is empty!')

兩種方法都可以判斷列表是否為空照弥,但是第二種方式更加簡潔,因此PEP 8鼓勵使用這種方式进副。

在if語句中这揣,使用 is not 比 no ... is in 好,當(dāng)你需要確定一個變量是否被賦值影斑,這里有兩種方法给赞,第一種是評估if語句中的x是不是為非None,如下所示:

# Recommended
if x is not None:
    return 'x exists!'

第二種方案是現(xiàn)在if語句中判斷x是不是None矫户,再進(jìn)行not操作:

# Not recommended
if not x is None:
    return 'x exists!'

兩種方法都可以判斷x是否為None片迅,但是第一種方式更加簡單,因此PEP 8鼓勵使用這種方式皆辽。

當(dāng)你想表達(dá)if x is not None時柑蛇,不要使用if x芥挣,有時候,你可能擁有一個函數(shù)帶有默認(rèn)值為None的參數(shù)耻台,當(dāng)對參數(shù)arg檢查是否帶有不同的值的時候經(jīng)常會犯下面這樣的錯誤:

# Not Recommended
if arg:
    # Do something with arg...

此代碼檢查的是arg是否為真空免,但相對的你要的是檢查是否為None,所以下面這樣寫比較好:

# Recommended
if arg is not None:
    # Do something with arg...

上面的錯誤會使得判斷是否為真和not None相等盆耽,你可以設(shè)置arg = []蹋砚,如上所述,空列表在Python中被認(rèn)為是False摄杂,因此坝咐,盡管參數(shù)被聲明為[],但條件并沒有滿足析恢,因此函數(shù)里面的代碼if聲明并不能被執(zhí)行墨坚。

用.startswith() 和.endswith()代替切片,如果你想檢查一段字符串的前綴或者后綴是否帶有字符串cat氮昧,這看起來使用列表切片似乎比較明智框杜,但是浦楣,列表切片容易出錯袖肥,您必須對前綴或后綴中的字符數(shù)進(jìn)行硬編碼,對于那些不太熟悉Python列表切片的人來說很難看清楚你想要實現(xiàn)的目的:

# Not recommended
if word[:3] == 'cat':
    print('The word starts with "cat"')

然而振劳,還是使用.startswith():可讀性比較高:

# Recommended
if word.startswith('cat'):
    print('The word starts with "cat"')

同樣的椎组,當(dāng)你想檢查字符串后綴也是同樣的原理,下面的示例概述了如何檢查字符串是否以jpg結(jié)尾:

# Not recommended
if file_name[-3:] == 'jpg':
    print('The file is a JPEG')

雖然結(jié)果是正確的历恐,但是符號有點復(fù)雜且難以閱讀寸癌,與之相對的,你可以用.endswith()實現(xiàn)弱贼,看下面例子:

# Recommended
if file_name.endswith('jpg'):
    print('The file is a JPEG')

就像上面的列出的編程建議蒸苇,我們目標(biāo)是編寫清晰以及可讀性較高的代碼,在Python中吮旅,有許多不同的方法可以實現(xiàn)相同的結(jié)果溪烤,因此有關(guān)選擇哪種方法的指南很有幫助。

When to Ignore PEP 8

什么時候忽略PEP 8庇勃?對于這個問題有個簡短的答案檬嘀,那就是永遠(yuǎn)不會

如果你嚴(yán)格遵循 PEP 8,就可以保證您擁有干凈责嚷、專業(yè)和可讀的代碼鸳兽。 這對你以及合作者和潛在雇主都有好處。

但是罕拂,PEP 8中的一些指導(dǎo)方針在以下實例中不適用:

  • 如果遵守PEP 8將破壞與現(xiàn)有軟件的兼容性
  • 如果你正在從事的工作的代碼與 PEP 8不一致
  • 如果代碼需要與舊版本的Python版本保持兼容

Tips and Tricks to Help Ensure Your Code Follows PEP 8

想要你的代碼兼容PEP 8揍异,那你得記住不少規(guī)范全陨,在開發(fā)代碼時,記住所有這些規(guī)則可能是一項艱巨的任務(wù)衷掷。特別是對老項目進(jìn)行更新以兼容PEP 8時烤镐,幸運的是,有些工具可以幫助你加快這一過程棍鳖,這里有兩個工具可以幫你強制兼容PEP 8規(guī)范:lintersautoformatters炮叶。

Linters

Linters是分析代碼和標(biāo)記錯誤的程序,他們可以為你如何修復(fù)錯誤提供建議渡处,在作為文本編輯器的擴展安裝時镜悉,Linters特別有用,因為它們標(biāo)記了你編程時的錯誤和格式問題医瘫。在本節(jié)中侣肄,您將看到Linters程序是如何工作的大綱,最后還有文本編輯器擴展的鏈接醇份。

對于Python代碼稼锅,最好的linters如下:

  • [pycodestyle](pycodestyle · PyPI)是一個根據(jù)PEP 8中的某些風(fēng)格約定來檢查Python代碼的工具
# 使用pip安裝pycodestyle
pip install pycodestyle

使用以下命令在終端中使用pycodestyle

pycodestyle code.py
code.py:1:17: E231 missing whitespace after ','
code.py:2:21: E231 missing whitespace after ','
code.py:6:19: E711 comparison to None should be 'if cond is None:'
  • flake8是一個結(jié)合了調(diào)試器,pyflakespycodestyle的工具
# 使用pip安裝flake8
pip install flake8

使用以下命令在終端中使用pycodestyle

flake8 code.py
code.py:1:17: E231 missing whitespace after ','
code.py:2:21: E231 missing whitespace after ','
code.py:3:17: E999 SyntaxError: invalid syntax
code.py:6:19: E711 comparison to None should be 'if cond is None:'

還顯示了輸出的示例僚纷。

::Note:額外輸出的一行表示語法錯誤矩距。::

這些也可用作Atom,Sublime Text怖竭,Visual Studio Code和VIM的擴展锥债。您還可以找到有關(guān)為Python開發(fā)設(shè)置Sublime Text和VIM的指南,以及Real Python中一些流行的文本編輯器的概述痊臭。

Autoformatters

Autoformatters可以重構(gòu)你的代碼以使其符合PEP 8規(guī)范哮肚,像black程序就可以根據(jù)PEP 8規(guī)范重新格式化你的代碼,一個很大的區(qū)別是它將行長度限制為88個字符广匙,而不是79個字符允趟。但是,您可以通過添加命令行標(biāo)志來覆蓋它鸦致,如下面的示例所示潮剪。

可以使用pip安裝black,它需要Python 3.6+

pip install black

它可以通過命令行運行蹋凝,就像Linters一樣鲁纠。假設(shè)你從名為code.py里面不符合PEP 8規(guī)范的代碼開始:

for i in range(0,3):
    for j in range(0,3):
        if (i==2):
            print(i,j)

你可以在命令行中運行如下代碼:

black code.py
reformatted code.py
All done!

code.py將會被重新格式化為如下所示:

for i in range(0, 3):
    for j in range(0, 3):
        if i == 2:
            print(i, j)

如果你想要更改行長度限制,則可以使用—line-length標(biāo)志:

black --line-length=79 code.py
reformatted code.py
All done!

另外兩個autoformatters分別為autopep8yapf鳍寂,它們和black操作類似改含。

另一個Real Python教程,由Alexander van Tol編寫的Python Code Quality: Tools & Best Practices詳細(xì)解釋了如何使用這些工具迄汛。

Conclusion

你現(xiàn)在已經(jīng)知道如何使用PEP 8中的指南編寫高質(zhì)量捍壤,可讀高的Python代碼骤视,雖然指南看起來很迂腐,但遵循它們可以真正改善你的代碼鹃觉,特別是在與潛在雇主或合作者共享代碼時专酗。

在本教程中你了解到:

  • 什么是PEP 8以及其存在的原因
  • 為什么你應(yīng)該編寫符合PEP 8標(biāo)準(zhǔn)的代碼
  • 如何編寫符合PEP 8的代碼

除此之外,您還了解了如何使用linters和autoformatters根據(jù)PEP 8指南檢查代碼盗扇。

如果你想了解更多祷肯,可以閱讀[full documentation](PEP 8 — Style Guide for Python Code | Python.org)或者瀏覽pep8.org,這些教程不僅提供了類似的信息同時也提供了很好的格式讓你閱讀疗隶,在這些文檔中佑笋,您將找到本教程未涉及的其余PEP 8指南。

image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末斑鼻,一起剝皮案震驚了整個濱河市蒋纬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌坚弱,老刑警劉巖蜀备,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異荒叶,居然都是意外死亡碾阁,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門停撞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瓷蛙,“玉大人,你說我怎么就攤上這事戈毒。” “怎么了横堡?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵埋市,是天一觀的道長。 經(jīng)常有香客問我命贴,道長道宅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任胸蛛,我火速辦了婚禮污茵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘葬项。我一直安慰自己泞当,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布民珍。 她就那樣靜靜地躺著襟士,像睡著了一般盗飒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上陋桂,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天逆趣,我揣著相機與錄音,去河邊找鬼嗜历。 笑死宣渗,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的梨州。 我是一名探鬼主播落包,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼摊唇!你這毒婦竟也來了咐蝇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤巷查,失蹤者是張志新(化名)和其女友劉穎有序,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體岛请,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡旭寿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了崇败。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盅称。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖后室,靈堂內(nèi)的尸體忽然破棺而出缩膝,到底是詐尸還是另有隱情,我是刑警寧澤岸霹,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布疾层,位于F島的核電站,受9級特大地震影響贡避,放射性物質(zhì)發(fā)生泄漏痛黎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一刮吧、第九天 我趴在偏房一處隱蔽的房頂上張望湖饱。 院中可真熱鬧,春花似錦杀捻、人聲如沸井厌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽旗笔。三九已至彪置,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蝇恶,已是汗流浹背拳魁。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留撮弧,地道東北人潘懊。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像贿衍,于是被迫代替她去往敵國和親授舟。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容