我的總結——全面的『Python編碼規範』

本文目的

『動態類型一時爽,代碼重構火葬場』,說的是:動態語言在初期開發比較爽,可是到後期維護起來比較困難。Python 做爲動態語言之一,天然也會有這樣的缺點。其實說『火葬場』,也沒有那麼嚴重,只要嚴格的遵照一組規範,也能作到『重構的時候,也同樣爽』。python

不以規矩不成方圓,規範天然是十分重要的,而在動態語言中,尤爲重要(不少人拿Python寫腳本,基本是爲所欲爲地寫,天然後期維護困難)。所謂『兵馬未動糧草先行』,咱們應該在寫代碼前,就作好充足的 「表面功夫」。編程

本文不涉及哪些

不作『文檔復讀機』。PEP 8中已經有了的,就不重複了,複述一些「低級(你們都知道了的)」 內容,沒啥意思。bash

不迷信權威,這裏指『Google Python Guide』,那是適合Google的規範,並非社區規範,其實我以爲這份規範既不完整,同時,淨是一些『衆人皆知』的內容,並不推薦之。框架

不搞宗教信仰,奉承實用主義。好比典型的『import this』,只有我一我的以爲不過是一堆空洞的廢話嗎,何況 Python 標準庫中不少地方,也沒有作到『import this』 中的『simple, explicit, and powerful』。每次各類文章(無節操的營銷文章,以各類培訓機構爲主體)說起這個東西,我真是渾身不自在&尷尬(寫代碼是很工程很嚴肅的事情,搞這種玄學幹嗎呢)。編程語言

適用範圍 & 原則

  • Python 2.7 - Python 3.x 。雖然官方宣佈了 Python 2的壽命是2020,並且彷佛如今 Python 3已是主流了。可是同窗,legacy code 可不是說去掉就去掉的,Python 2仍然會存在至關長一段時間。何況,Python 3 有相對於 Python 2 的 『killer feature』 嗎,並無。
  • 以 PEP 8 爲藍本,牢牢團結在 PEP 8 周圍。任何非官方的文檔,只是參考之(同理,本文亦是某種參考資料)。
  • PEP 8 已經有了的,就不要重複了。

規範

【強制 + 強制】【挑選『靜態檢查』工具,並自始至終都嚴格使用】

簡單來講,就是:
1. Pylint
2. Flake8
3. pytest

一開始就要使用,而且從嚴使用(發點時間瞭解這幾個工具,帶來的收益是無限的,若是你是比較正式的項目的話)。
複製代碼

【強制+】【多寫UT】

其餘編程語言,同理。

有一份UT在手,重構起來,內心放心不少。

Python的話,只須要了解unittest就夠了,pytest也能夠。
複製代碼

【強制】【文件編碼 & Unicode】

PS:下面這幾條,能幫你避免不少無聊的編碼解碼問題,因此我以爲很重要ide

  1. 使用 4 空格縮進,禁用任何 TAB 符號
  2. 源碼文件使用 UTF-8 無 BOM 編碼格式
  3. 老是使用 Unix \n 風格換行符
  4. 在每個 py 文件頭,都添加以下內容:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
 

# 只導入 future 空間的這兩個特性就夠了,其餘特性容易形成其餘方面的『不兼容』,沒有使用的必要性。
from __future__ import (absolute_import, unicode_literals)

因此,你須要瞭解 editorconfig 這個東西,從根源上統一規範,不符合規範的,直接拒絕 PUSH or MERGE
複製代碼

###【強制】【命名】工具

  1. class,function 該如何命名,不贅述,嚴格照着PEP8作就好了,不用多想。
  2. 全局變量(全局變量,通常是常量,咱們認爲:凡是全局的,都是常量),應該始終使用全大寫,如:
GLOBAL_PUBLIC = "G1"
_GLOBAL_PRIVATE = "G2"

class Person:
    _GLOBAL_IN_CLASS = 'G3'
    

按照這條要求,其實不少庫or開源庫,都是不符合要求的。爲何這麼強硬呢?
Python中的變量定義,是不分『聲明』、『定義』、『初始化』、『賦值』這幾個概念的,因此一個
a = 1
若是沒有上下文,你是很難肯定其做用域的,也很難肯定 這究竟是初始化仍是賦值(a已經存在過),
若是全局變量還不用全大寫,帶來的麻煩只會更多。

若是始終堅持這個原則,將會給代碼的可讀性帶來極大提高。
複製代碼

【強制】定義枚舉,始終加 Enum後綴;定義異常始終加Exception後綴;定義mixin,始終加Mixin後綴,如

class DirectionEnum:
    UP = 1
    DOWN = 2
    
    
class MyException(Exception):
    pass
class MyError(Exception):
    pass
    
class SomeMixin:
    pass
複製代碼

【強制】【強化private的概念】

即:最小知識原則,對外暴露的東西越少越好

翻譯成大白話就是:
1. 實例屬性,通常定義成private的
2. class,對外提供的方法越少越好
3. module,對外提供的接口越少越好
4. package,對外提供的 module 越少越好

翻譯成代碼就是:
1. 項目佈局
package/
    __init__.py
    _private_mod.py
    public_mod.py    
    
2. 某模塊內容
public_mod.py
PUBLIC_GLOBAL = 'G1'
_PRIVATE_GLOBAL = 'G2'
class _Class:
    pass
class PublicClass:
    _PRIVATE_GLOBAL = 'G3'
    
    def __init__(self, name,age):
        self._name = name
        self._age = age
    def public_method(self):
        pass
    def _private(self):
        pass
        

全部東西,一開始就要定義成私有的,等到確實須要開放訪問了,纔開放出去。
複製代碼

【強制&重要】【關注公開接口的複雜性】

最好的接口是這樣的,調用者無腦使用
def interface():
    pass
    
次等接口是這樣的
def interface(param1):
    pass
    
次次等接口是這樣的
def interface(p1, p2):
    pass

最大忍受限度的接口是這樣的
def interface(p1, p2, p3='SOME DEFAULT'):
    pass
def interface(p1, *args):
    pass
    
不可接受的接口是這樣的
def interface(p1, p2, **kwargs):
    pass

使人無語的接口是這樣的
def interface(*args, **kwargs):  
# 儘可能不要使用 **kwargs, 某些流行庫有這樣的毛病,我是以爲:極大地增長了調用者的心理負擔,反映了接口設計者的懶惰
    pass
    
一直以爲,**kwargs只適用於極少數明確的場合,而且須要輔以很明確的文檔說明(解釋爲何要使用),然而現實是,這個
特性已經被你們濫用了,有必要單獨說明之。
複製代碼

PS:我一直以爲,濫用 **kwargs 的API,幾乎都不是好 API,無形增長心理負擔。佈局

【推薦】【以package去設計命名空間,而不是基於module】

【推薦】【瞭解以下內容】

__init__.py 的做用

__main__.py 的做用

if __name__ == '__main__': 的做用

Python的命名空間加載機制,即:sys.path sys.modules 的內容
複製代碼

【推薦】【合理設計項目目錄結構】

若是是使用某種框架(如Django),那麼按照框架的規範來;若是是「非框架」項目,則按照以下結構

project
    project/
        __init__.py
        core/
        utils/
        constants/
        
        
        __main__.py
        
    tests/
    docs/
    examples/
    README.md
    .pylintrc
    .flake8
    
複製代碼
相關文章
相關標籤/搜索