『動態類型一時爽,代碼重構火葬場』,說的是:動態語言在初期開發比較爽,可是到後期維護起來比較困難。Python 做爲動態語言之一,天然也會有這樣的缺點。其實說『火葬場』,也沒有那麼嚴重,只要嚴格的遵照一組規範,也能作到『重構的時候,也同樣爽』。python
不以規矩不成方圓,規範天然是十分重要的,而在動態語言中,尤爲重要(不少人拿Python寫腳本,基本是爲所欲爲地寫,天然後期維護困難)。所謂『兵馬未動糧草先行』,咱們應該在寫代碼前,就作好充足的 「表面功夫」。編程
不作『文檔復讀機』。PEP 8中已經有了的,就不重複了,複述一些「低級(你們都知道了的)」 內容,沒啥意思。bash
不迷信權威,這裏指『Google Python Guide』,那是適合Google的規範,並非社區規範,其實我以爲這份規範既不完整,同時,淨是一些『衆人皆知』的內容,並不推薦之。框架
不搞宗教信仰,奉承實用主義。好比典型的『import this』,只有我一我的以爲不過是一堆空洞的廢話嗎,何況 Python 標準庫中不少地方,也沒有作到『import this』 中的『simple, explicit, and powerful』。每次各類文章(無節操的營銷文章,以各類培訓機構爲主體)說起這個東西,我真是渾身不自在&尷尬(寫代碼是很工程很嚴肅的事情,搞這種玄學幹嗎呢)。編程語言
簡單來講,就是:
1. Pylint
2. Flake8
3. pytest
一開始就要使用,而且從嚴使用(發點時間瞭解這幾個工具,帶來的收益是無限的,若是你是比較正式的項目的話)。
複製代碼
其餘編程語言,同理。
有一份UT在手,重構起來,內心放心不少。
Python的話,只須要了解unittest就夠了,pytest也能夠。
複製代碼
PS:下面這幾條,能幫你避免不少無聊的編碼解碼問題,因此我以爲很重要ide
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# 只導入 future 空間的這兩個特性就夠了,其餘特性容易形成其餘方面的『不兼容』,沒有使用的必要性。
from __future__ import (absolute_import, unicode_literals)
因此,你須要瞭解 editorconfig 這個東西,從根源上統一規範,不符合規範的,直接拒絕 PUSH or MERGE
複製代碼
###【強制】【命名】工具
GLOBAL_PUBLIC = "G1"
_GLOBAL_PRIVATE = "G2"
class Person:
_GLOBAL_IN_CLASS = 'G3'
按照這條要求,其實不少庫or開源庫,都是不符合要求的。爲何這麼強硬呢?
Python中的變量定義,是不分『聲明』、『定義』、『初始化』、『賦值』這幾個概念的,因此一個
a = 1
若是沒有上下文,你是很難肯定其做用域的,也很難肯定 這究竟是初始化仍是賦值(a已經存在過),
若是全局變量還不用全大寫,帶來的麻煩只會更多。
若是始終堅持這個原則,將會給代碼的可讀性帶來極大提高。
複製代碼
class DirectionEnum:
UP = 1
DOWN = 2
class MyException(Exception):
pass
class MyError(Exception):
pass
class SomeMixin:
pass
複製代碼
即:最小知識原則,對外暴露的東西越少越好
翻譯成大白話就是:
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,無形增長心理負擔。佈局
__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
複製代碼