說說Python編碼規範

前言

        已有近兩個月沒有發表過文章了,前段時間外甥和女兒過來這邊渡暑假,日常晚上和週末時間都陪着她們了,趁這個週末有空,再抽空再把這塊拾起來。
         這麼久沒寫了,再次拿起鍵盤,想一想,發表些什麼呢,想起上次公司的代碼評審委員會下週其中一個議題是關於Python編碼規範的整理,那就趁熱打鐵,整理一份關於Python編碼規範的文章,也爲那些寫Python的人,提供一些編碼注意的一些事項或者說是參考吧。python

編碼規範的做用

        規範故明思義,就是經過不斷的總結,吸收好的點,從而造成的一份你們共同須要遵照的行爲契約,
網上有不少版本的編碼規範,基本上都是遵循 PEP8 的規範。那麼什麼是PEP8呢?
        PEP是 Python Enhancement Proposal 的縮寫,簡單來講,是python加強建議書的意思。它描述了Python編程風格的方方面面。在遵照這個文檔的條件下,不一樣程序員編寫的Python代碼能夠保持最大程度的類似風格。
這樣就易於閱讀,易於在程序員之間交流。程序員


下面就說說Python編碼時,應該遵照的編碼規範有哪些。算法

編碼需遵照的規範

編碼

  • 全部的 Python 腳本文件都應在文件頭標上以下標識或其兼容格式的標識: # -- coding:utf-8 --sql

分號

  • 不要在行尾加分號, 也不要用分號將兩條命令放在同一行。編程

換行

  • 常規下,每一行代碼控制在 80 字符之內json

  • 如下狀況除外:flask

    • 長的導入模塊語句框架

    • 註釋裏的URLsocket

  • 使用 \ 或 () 控制換行,舉例:編輯器

      def foo(first, second, third, fourth, fifth,
              sixth, and_some_other_very_long_param):
          user = User.objects.filter_by(first=first, second=second, third=third) \
              .skip(100).limit(100) \
              .all()
    
      text = ('Long strings can be made up ''of several shorter strings.')

     

    若是行長到連第一個括號內的參數都放不下,則每一個元素都單獨佔一行:
  • 摺疊長行的首選方法是使用Python支持的圓括號、方括號(brackets)和花括號(braces)內的行延續。可是有時也能夠適當使用反斜槓 \ 。

括號

  • 寧缺毋濫的使用括號

  • 除非是用於實現行鏈接, 不然不要在返回語句或條件語句中使用括號. 不過在元組兩邊使用括號是能夠的.

    推薦: if foo:
             bar()while x:
             x = bar()if x and y:
             bar()if not x:
             bar()return foo         for (x, y) in dict.items(): ..
    不推薦:  if (x):
             bar()if not(x):
             bar()return (foo)
 

縮進

  • 用4個空格來縮進代碼

  • 絕對不要用tab, 也不要tab和空格混用,不然容易出現 IndentationError

  • 使用任何編輯器寫 Python,請把一個 tab 展開爲 4 個空格

空行

  • 頂級定義之間空兩行, 好比函數或者類定義. 方法定義, 類定義與第一個方法之間, 都應該空一行. 函數或方法中, 某些地方要是你以爲合適, 就空一行.

  • function 和 class 頂上兩個空行

  • class 的 method 之間一個空行

  • 函數內邏輯無關的段落之間空一行,不要過分使用空行

  • 不要把多個語句寫在一行,而後用 ; 隔開

  • if/for/while 語句中,即便執行語句只有一句,也要另起一行

  • 在類、函數的定義間加空行;

  • 在import不一樣種類的模塊間加空行;

  • 在函數中的邏輯段落間加空行,即把相關的代碼緊湊寫在一塊兒,做爲一個邏輯段落,段落間以空行分隔;

空格

  • 整體原則,避免沒必要要的空格。

  • 各類右括號前不要加空格。

  • 函數的左括號前不要加空格。如Func(1)。

  • 序列的左括號前不要加空格。如list[2]。

  • 操做符左右各加一個空格,不要爲了對齊增長空格。

  • 函數默認參數使用的賦值符左右省略空格。

  • 不要將多句語句寫在同一行,儘管使用‘;’容許。

  • if/for/while語句中,即便執行語句只有一句,也必須另起一行。

  • 在二元算術、邏輯運算符先後加空格如:a = b + c

  • 在 list, dict, tuple, set, 參數列表的 , 後面加一個空格

  • 在 dict 的 : 後面加一個空格

  • 在註釋符號 # 後面加一個空格,可是 #!/usr/bin/python 的 # 後不能有空格

  • 操做符兩端加一個空格,如 +, -, *, /, |, &, =

  • 接上一條,在參數列表裏的 = 兩端不須要空格

  • 括號((), {}, [])內的兩端不須要空格

  • 括號內不要有空格.

  • 不要在逗號, 分號, 冒號前面加空格, 但應該在它們後面加(除了在行尾).

    推薦: if x == 4:print x, y
         x, y = y, x
    不推薦:  if x == 4 :print x , y
     x , y = y , x

 

  • 在二元操做符兩邊都加上一個空格, 好比賦值(=), 比較(==, <, >, !=, <>, <=, >=, in, not in, is, is not), 布爾(and, or, not). 至於算術操做符兩邊的空格該如何使用, 須要你本身好好判斷. 不過兩側務必要保持一致.

    推薦: x == 1
    不推薦:  x<1

 

  • 當’=’用於指示關鍵字參數或默認參數值時, 不要在其兩側使用空格.

      推薦: def complex(real, imag=0.0): return magic(r=real, i=imag)
      不推薦:  def complex(real, imag = 0.0): return magic(r = real, i = imag)

     

  • 不要用空格來垂直對齊多行間的標記, 由於這會成爲維護的負擔(適用於:, #, =等):

    推薦:
         foo = 1000  # 註釋
         long_name = 2  # 註釋不須要對齊

         dictionary = {"foo": 1,"long_name": 2,}
    不推薦:
         foo       = 1000  # 註釋
         long_name = 2     # 註釋不須要對齊

         dictionary = {"foo"      : 1,"long_name": 2,}

 

Shebang

  • 大部分.py文件沒必要以#!做爲文件的開始

  • 程序的main文件應該以 #!/usr/bin/python2或者 #!/usr/bin/python3開始.

 

補充知識: 此處解釋一下何爲Shebang,Shebang就是
是一個由井號和歎號構成的字符串行(#!), 其出如今文本文件的第一行的前兩個字符. 在文件中存在Shebang的狀況下,
類Unix操做系統的程序載入器會分析Shebang後的內容, 將這些內容做爲解釋器指令, 並調用該指令,
並將載有Shebang的文件路徑做爲該解釋器的參數. 例如, 以指令#!/bin/sh開頭的文件在執行時會實際調用/bin/sh程序.)#!先用於幫助內核找到Python解釋器, 可是在導入模塊時, 將會被忽略. 所以只有被直接執行的文件中才有必要加入#!

註釋

  • 爲了提升可讀性, 塊註釋和行註釋註釋應該至少離開代碼2個空格.

  • 塊註釋,在一段代碼前增長的註釋。在‘#’後加一空格。段落之間以只有‘#’的行間隔。好比:

      # Description : Module config.
      # 
      # Input : None
      #
      # Output : None

     

  • 行註釋,在一句代碼後加註釋。好比:x = x + 1            # Increment x

  • 爲全部的共有模塊、函數、類、方法寫docstrings;非共有的沒有必要,可是能夠寫註釋(在def的下一行)。

  • 若是docstring要換行

      """Return a foobang
    
      Optional plotz says to frobnicate the bizbaz first.
    
      """

     

  • 文檔字符串 docstring, 是 package, module, class, method, function 級別的註釋,能夠經過doc 成員訪問到,註釋內容在一對 「」」 符號之間

  • function, method 的文檔字符串應當描述其功能、輸入參數、返回值,若是有複雜的算法和實現,也須要寫清楚

  • 優先使用英文寫註釋,英文很差所有寫中文,不然更加看不懂

  • 註釋塊:註釋塊一般應用於跟隨其後的一些 (或者所有) 代碼,並和這些代碼有着相同的縮進 層次。註釋塊中每行以 ‘#’ 和一個空格開始 (除非它是註釋內的縮進文本)。
    註釋塊內的段落以僅含單個 ‘#’ 的行分割

  • 行內註釋:一個行內註釋是和語句在同一行的註釋。行內註釋應該至少用兩個空格和語句分開。 它們應該以一個 ‘#’ 和單個空格開始。

異常

  • 不要輕易使用 try/except

  • except 後面須要指定捕捉的異常,裸露的 except 會捕捉全部異常,意味着會隱藏潛在的問題

  • 能夠有多個 except 語句,捕捉多種異常,分別作異常處理

  • 使用 finally 子句來處理一些收尾操做

  • try/except 裏的內容不要太多,只在可能拋出異常的地方使用

  • 從 Exception 而不是 BaseException 繼承自定義的異常類

Class(類)

  • 使用 super 調用父類的方法

  • 支持多繼承,即同時有多個父類,建議使用 Mixin

  • 若是一個類不繼承自其它類, 就顯式的從object繼承. 嵌套類也同樣.

   
推薦: 
    class SampleClass(object):
        pass
    class OuterClass(object):
        pass
    class InnerClass(object):
        pass

    class ChildClass(ParentClass):
    """Explicitly inherits from another class already."""
        pass
    不推薦: 
    class SampleClass:
        pass
    class OuterClass:
        pass   
    class InnerClass:
        pass

 

這是繼承自 object 是爲了使屬性(properties)正常工做, 而且這樣能夠保護你的代碼, 使其不受Python 3000的一個特殊的潛在不兼容性影響. 這樣作也定義了一些特殊的方法, 這些方法實現了對象的默認語義, 包括 newinitdelattrgetattributesetattrhashrepr, and str .

引號

  • 在同一個文件中, 保持使用字符串引號的一致性. 使用單引號’或者雙引號」之一用以引用字符串, 並在同一文件中沿用. 在字符串內可使用另一種引號,

  • 爲多行字符串使用三重雙引號」」」而非三重單引號’’’. 當且僅當項目中使用單引號’來引用字符串時, 纔可能會使用三重’’’爲非文檔字符串的多行字符串來標識引用. 文檔字符串必須使用三重雙引號」」」. 不過要注意, 一般用隱式行鏈接更清晰, 由於多行字符串與程序其餘部分的縮進方式不一致.

文件和sockets

  • 在文件和sockets結束時, 顯式的關閉它.

  • 推薦使用 「with」語句 以管理文件:

     with open("hello.txt") as hello_file:     
         for line in hello_file:         
             print line    

     

  • 對於不支持使用」with」語句的相似文件的對象,使用 contextlib.closing():

      import contextlib  with contextlib.closing(urllib.urlopen("http://www.python.org/")) as front_page:      
        for line in front_page:         
             print line    

     

TODO註釋

  • TODO註釋應該在全部開頭處包含」TODO」字符串, 緊跟着是用括號括起來的你的名字, email地址或其它標識符. 而後是一個可選的冒號. 接着必須有一行註釋, 解釋要作什麼

  • 若是你的TODO是」未來作某事」的形式, 那麼請確保你包含了一個指定的日期(「2009年11月解決」)或者一個特定的事件(「等到全部的客戶均可以處理XML請求就移除這些代碼」)

import導入格式

        • 每一個導入應該獨佔一行

            推薦: import os       
              import sys
            from flask import Flask, render_template, jsonify
          不推薦: import os, sys 

           

  • 導入總應該放在文件頂部, 位於模塊註釋和文檔字符串以後, 模塊全局變量和常量以前. 導入應該按照從最通用到最不通用的順序分組:

    • 標準庫導入

    • 第三方庫導入

    • 應用程序指定導入

  • 全部 import 儘可能放在文件開頭,在 docstring 下面,其餘變量定義的上面

  • 不要使用 from foo imort *

  • 爲了不可能出現的命名衝突,可使用 as 或導入上一級命名空間

  • 不要出現循環導入(cyclic import)

命名

命名參考形式:
module_name, package_name, ClassName, method_name, ExceptionName, function_name, GLOBAL_VAR_NAME, instance_var_name, function_parameter_name, local_var_name.

  • 應該避免的名稱

    • 單字符名稱, 除了計數器和迭代器.

    • 包/模塊名中的連字符(-)

    • 雙下劃線開頭並結尾的名稱(Python保留, 例如init)

  • 命名約定

    • 所謂」內部(Internal)」表示僅模塊內可用, 或者, 在類內是保護或私有的.

    • 用單下劃線(_)開頭表示模塊變量或函數是protected的(使用import * from時不會包含).

    • 用雙下劃線(__)開頭的實例變量或方法表示類內私有.

    • 將相關的類和頂級函數放在同一個模塊裏. 不像Java, 不必限制一個類一個模塊.

    • 對類名使用大寫字母開頭的單詞(如CapWords, 即Pascal風格), 可是模塊名應該用小寫加下劃線的方式(如lower_with_under.py). 儘管已經有不少現存的模塊使用相似於CapWords.py這樣的命名, 但如今已經不鼓勵這樣作, 由於若是模塊名碰巧和類名一致, 這會讓人困擾.

  • 儘可能單獨使用小寫字母‘l’,大寫字母‘O’等容易混淆的字母。

  • 模塊命名儘可能短小,使用所有小寫的方式,可使用下劃線。

  • 包命名儘可能短小,使用所有小寫的方式。

  • 類的命名使用CapWords的方式,模塊內部使用的類採用_CapWords的方式。

  • 異常命名使用CapWords+Error後綴的方式。

  • 全局變量儘可能只在模塊內有效,相似C語言中的static。實現方法有兩種,一是all機制;二是前綴一個下劃線。

  • 函數命名使用所有小寫的方式,可使用下劃線。

  • 常量命名使用所有大寫的方式,可使用下劃線。

  • 類的屬性(方法和變量)命名使用所有小寫的方式,可使用下劃線。

  • 類的屬性有3種做用域public、non-public和subclass API,能夠理解成C++中的public、private、protected,non-public屬性前,前綴一條下劃線。

  • 類的屬性若與關鍵字名字衝突,後綴一下劃線,儘可能不要使用縮略等其餘方式。

  • 爲避免與子類屬性命名衝突,在類的一些屬性前,前綴兩條下劃線。好比:類Foo中聲明a,訪問時,只能經過Foo._Fooa,避免歧義。若是子類也叫Foo,那就無能爲力了。

  • 類的方法第一個參數必須是self,而靜態方法第一個參數必須是cls。

  • 使用有意義的,英文單詞或詞組,絕對不要使用漢語拼音

  • package/module 名中不要出現 -

Main方法

  • 全部的頂級代碼在模塊導入時都會被執行. 要當心不要去調用函數, 建立對象, 或者執行那些不該該在使用pydoc時執行的操做.

字符串

  • 使用字符串的 join 方法拼接字符串

  • 使用字符串類型的方法,而不是 string 模塊的方法

  • 使用 startswith 和 endswith 方法比較前綴和後綴

  • 使用 format 方法格式化字符串

比較

  • 空的 list, str, tuple, set, dict 和 0, 0.0, None 都是 False

  • 使用 if some_list 而不是 if len(some_list) 判斷某個 list 是否爲空,其餘類型同理

  • 使用 is 和 is not 與單例(如 None)進行比較,而不是用 == 和 !=

  • 使用 if a is not None 而不是 if not a is None

  • 用 isinstance 而不是 type 判斷類型

  • 不要用 == 和 != 與 True 和 False 比較(除非有特殊狀況,如在 sqlalchemy 中可能用到)

  • 使用 in 操做:

  • 用 key in dict 而不是 dict.has_key()

      不推薦 if d.has_key(k):
      do_something()
    
      推薦 if key in d:
      do_something()

     

  • 用 set 加速 「存在性」 檢查,list 的查找是線性的,複雜度 O(n),set 底層是 hash table, 複雜度 O(1),但用 set 須要比 list 更多內存空間

代碼編排

  • 縮進。4個空格的縮進(編輯器均可以完成此功能),不使用Tap,更不能混合使用Tap和空格。

  • 每行最大長度79,換行可使用反斜槓,最好使用圓括號。換行點要在操做符的後邊敲回車。

  • 類和top-level函數定義之間空兩行;類中的方法定義之間空一行;函數內邏輯無關段落之間空一行;其餘地方儘可能不要再空行。

文檔編排

  • 模塊內容的順序:模塊說明和docstring—import—globals&constants—其餘定義。其中import部分,又按標準、三方和本身編寫順序依次排放,之間空一行。

  • 不要在一句import中多個庫,好比import os, sys不推薦。

  • 若是採用from XX import XX引用庫,能夠省略‘module.’,都是可能出現命名衝突,這時就要採用import XX

編碼建議

  • 編碼中考慮到其餘python實現的效率等問題,好比運算符‘+’在CPython(Python)中效率很高,都是Jython中卻很是低,因此應該採用.join()的方式。

  • 儘量使用‘is’‘is not’取代‘==’,好比if x is not None 要優於if x。

  • 使用基於類的異常,每一個模塊或包都有本身的異常類,此異常類繼承自Exception。

  • 異常中不要使用裸露的except,except後跟具體的exceptions。

  • 異常中try的代碼儘量少。

  • 使用startswith() and endswith()代替切片進行序列前綴或後綴的檢查。好比:

    推薦:  if foo.startswith('bar'):
    不推薦:  if foo[:3] == 'bar':

     

  • 使用isinstance()比較對象的類型。好比

    推薦:  if isinstance(obj, int): 優於
    不推薦:  if type(obj) is type(1):

     

  • 判斷序列空或不空,有以下規則

    Yes:  if not seq:if seq:
    優於
    No:  if len(seq)if not len(seq)

     

  • 字符串不要以空格收尾。

  • 二進制數據判斷使用 if boolvalue的方式。

  • 使用列表表達式(list comprehension),字典表達式(dict comprehension, Python 2.7+) 和生成器(generator)

  • dict 的 get 方法能夠指定默認值,但有些時候應該用 [] 操做,使得能夠拋出 KeyError

  • 使用 for item in list 迭代 list, for index, item in enumerate(list) 迭代 list 並獲取下標

  • 使用內建函數 sorted 和 list.sort 進行排序

  • 適量使用 map, reduce, filter 和 lambda,使用內建的 all, any 處理多個條件的判斷

  • 使用裝飾器(decorator)

  • 使用 with 語句處理上下文

  • 使用 logging 記錄日誌,配置好格式和級別

  • 閱讀優秀的開源代碼,如 Flask 框架, Requests

  • 不要重複造輪子,查看標準庫、PyPi、Github、Google 等使用現有的優秀的解決

 

 

好了,時間也不早了,今天就到此爲止吧,若是以爲本文對你有點用的話,就邀請身邊的人關注起吧~

公衆號爲:mikezhou_talk

相關文章
相關標籤/搜索