1、structuring of project
首先我們需要去考慮如何更好地利用Python的特性來創造簡潔、高效的代碼。在C++/Java等工業界的語言中,“結構化”意味着通過編寫簡潔的代碼,正如文件系統中文件和目錄的組織一樣,使代碼的邏輯和依賴更清晰。
在任何項目開始之前我們需要做一個規劃,例如哪個函數應該深入到哪個模塊,數據在項目中應該如何流轉,什麼功能和函數應該組合或獨立,最終看到的產品是怎樣的等
2、structuring of repository
在一個健康的開發週期中,代碼風格,API設計和自動化都是非常關鍵的,如果目錄結構是一團糟,沒有清晰的結構,可能需要到處尋找才能找到所需要的內容或文檔,爲讓每一位開發者能夠熟悉它的每一個角落和細節,擁有良好的細節,良好的佈局將會事半功倍
for example:README.rst
LICENSE-----------------------------在這個文件中要有完整的許可說明和授權。
setup.py------------------------------- 打包和發佈管理
requirements.txt--------------------- 它應該指明完整工程的所有依賴包: 測試, 編譯和文檔生成。
scanhosts/__init__.py--------------------------核心代碼
scanhosts/views.py
dashboard/views.py
mydevops/settings.py---------------------配置文件
docs/index.rst------------------------參考文檔
tests/test_basic.py----------------- 集合與單元測試,應該把測試例子放到模塊裏面,但這樣會增加用戶使用的複雜度;而且添加測試模塊將導致需要額外的依賴和運行環境。
tests/test_advanced.py
manage.py-------------------------------------------- 常規管理腳本,即使不是C/C++寫的項目也有makefile文件,make對於定義常規的管理任務是非常有用的工具
3、Django Applications
不要錯誤的使用Django自帶的應用模板創建項目,而導致目錄結構非常糟糕
通常:django admin.py startproject samplesite
生成:
README.rst
mydevops/manage.py
mydevops/mydevops/settings.py
mydevops/mydevops/wsgi.py
mydevops/mydevops/scanhosts/models.py
避免上述類似操作,因爲相對路徑使girrit工具等及開發者都容易誤解,這種沒有必要的嵌套對任何人都無益
正確:django-admin.py startproject samplesite .
生成:
README.rst
manage.py
samplesite/settings.py
samplesite/wsgi.py
samplesite/sampleapp/models.py
4、modules
Python的modules是最主要的抽象層之一,抽象層允許將代碼分成不同的部分,每個部分包含相關的數據與功能,在項目中,一層控制用戶操作的相關接口,另一層處理底層數據的操作。最自然的分開這兩層的方式是,在一層文件裏重組所有的功能接口,並將所有底層操作封裝到另一個文件中,這種情況下,接口文件需要導入封裝底層操作的文件,可通過import和from import來完成。
爲遵守QJHK+J02009-2018+Python編程規範,規定模塊名稱要短、儘量使用小寫,並避免使用特殊符號,例如.或?,scanhosts.db.py,該命名方式將會阻礙python的模塊查找功能,python將認爲需要在scanhosts文件夾中找到db.py,應儘量保持模塊名稱簡單,無需分開單詞,不要使用下滑線命名空間,而是使用子模塊
# Good
import library.plugin.foo
# Bad
import library.foo_plugin
除了以上的命名限制外,python文件成爲模塊沒有其他特殊的要求,但爲了合理的使用這個觀念並避免問題,需要理解import的原理機制。Import module語句將尋找合適的文件,調用目錄下的module.py,若沒有找到,python解釋器將遞歸的在pythonpath環境變量中查找文件,若仍沒有找到,將拋出ImportError異常,一旦找到module.py,python解釋器將在隔離的作用域中執行這個模塊,所有頂層語句都會執行,包括其他的引用。方法與類的引用將會存儲到模塊的字典中,然後,這個模塊的變量,方法和類通過命名空間暴漏給調用方,這是python中特別有用且核心的概念
5、packages
Python提供非常簡單的包管理系統,即簡單地將模塊管理機制擴展到一個目錄上(目錄擴 展爲包)。
任意包含 __init__.py 文件的目錄都被認爲是一個Python包
一個常見的問題是往 __init__.py 中加了過多代碼,隨着項目的複雜度增長, 目錄結構越來越深,子包和更深嵌套的子包可能會出現。在這種情況下,導入多層嵌套 的子包中的某個部件需要執行所有通過路徑裏碰到的 __init__.py 文件。如果 包內的模塊和子包沒有代碼共享的需求,使用空白的 __init__.py 文件是正常 甚至好的做法。
6、code style
我們提倡最明確和最直接的編碼方式
6.1、儘可能使用 is/is not 取代 ‘==’
6.2、使用基於類的異常
6.3、異常中不要裸露except、後面跟上具體的exception
6.4、異常中try代碼儘可能少
6.5、python2中十進制浮點精度運算不準確,可使用decimal和fraction模塊,例如價格校驗
6.6、urlib2多線程訪問會死鎖,可使用urllib3模塊
6.7、避免使用while true,避免死循環
6.8、函數if else不要多於3-4個,保持代碼和邏輯清晰,複雜段落每個小的邏輯段落要有空行和註釋
6.9、避免使用全局變量
6.10、複合語句因其簡潔和表達性受到推崇,但不應同一行代碼中寫兩條獨立的語句。
7、Python 代碼遵循 PEP 8 ,這也有助於在與其他開發人員 一起工作時使代碼更加具有可持續性
7.1、檢查變量是否等於常量
不需要明確的比較True或None,僅需放在if語句中(儘可能使用is/is not 取代 '==')
bad:
if attr == True:
print 'True!'
if attr == None:
print 'attr is None!'
Good:
if attr:
print 'attr is truthy!'
if not attr:
print 'attr is falsey!'
# or, since None is considered false, explicitly check for it
if attr is None:
print 'attr is None!'
7.2、訪問字典元素
不要使用 dict.has_key() 方法。取而代之,使用 x in d 語法,或者 將一個默認參數傳遞給 dict.get()。
Bad:
d = {'hello': 'world'}
if d.has_key('hello'):
print d['hello'] # 打印 'world'
else:
print 'default_value'
Good:
d = {'hello': 'world'}
print d.get('hello', 'default_value') # 打印 'world'
print d.get('thingy', 'default_value') # 打印 'default_value'
# Or:
if 'hello' in d:
print d['hello']
7.3:過濾列表:
在迭代列表的過程中,永遠不要從列表中移除元素
# 過濾大於 4 的元素
a = [3, 4, 5]
for i in a:
if i > 4:
a.remove(i)
不要在列表中多次遍歷
while i in a:
a.remove(i)
7.4:在列表中修改值
bad:
賦值永遠不會創建新對象。如果兩個或多個變量引用相同的列表,則修改其中一個變量意味着將修改所有變量
# 所有的列表成員都加 3
a = [3, 4, 5]
b = a # a 和 b 都指向一個列表獨享
for i in range(len(a)):
a[i] += 3 # b[i] 也改變了
good:
創建一個新的列表對象並保留原始列表對象會更安全
a = [3, 4, 5]
b = a
# 給變量 "a" 賦值新的列表,而不改變 "b"
a = [i + 3 for i in a]
# 或者 (Python 2.x):
a = map(lambda i: i + 3, a)
# 或者 (Python 3.x):
a = list(map(lambda i: i + 3, a))
使用 enumerate() 獲得列表中的當前位置的計數,
使用 enumerate() 函數比手動維護計數有更好的可讀性。而且,它對迭代器 進行了更好的優化
a = [3, 4, 5]
for i, item in enumerate(a):
print i, item
# 打印
# 0 3
# 1 4
# 2 5
讀取文件:
使用with open語法來讀取文件,它將會爲您自動關閉文件
bad:
f = open('file.txt')
a = f.read()
print a
f.close()
Good:
with open('file.txt') as f:
for line in f:
print line
儘量使用with語句,即使是在 with 的區塊中拋出異常,也能確保關閉文件。
8、Django工程注意規範
8.1.各個APP獨立,做到項目的模塊分明。
from mydevops.scanhosts.models import mycat
該例子將項目名稱加入其中是不合適,缺點在於:應用和項目變成了緊耦合,無法將應用輕易變得可重用。如果將來要換一個項目名稱,那你可有得受了。
推薦的做法是
from scanhosts.models import mycat
8.2. 不要硬編碼 MEDIA_ROOT,STATIS_ROOT,
在python中關於url的處理,有些原則,比如不要硬編碼,處理成UNIX,WINDOWS兼容的格式,和進行空格的處理等
MEDIA_ROOT = 'E:\\test\mugshot\\'
STATIC_ROOT = '/VAR/TEMP/STATIC'
這種做法有很多問題,比如機器遷移,和 應用的移動都會影響。
SITE_ROOT = os.path.realpath(os.path.dirname(__file__))
MEDIA_ROOT = os.path.join(SITE_ROOT, 'appmedia')
TEMPLATE_DIRS = ( os.path.join(SITE_ROOT, 'templates'),)
這樣的寫法比較可擴展性。
8.3,不要將業務邏輯代碼寫到視圖裏
雖然很多例子,它們把邏輯都寫在了views.py裏,因爲這樣不利於單元測試,不利於重用代碼。
那我們的業務邏輯應該放哪裏呢?推薦放到模型裏或者單獨建立一個輔助模塊。
當然,獲取數據列表的代碼是可以放到視圖裏面的。
8.4,部署時別忘記將DEBUG設置成False
8.5、合理配置和使用URL
不要將URL全都配置在一個urls.py文件中,比如:
urlpatterns = [
url(r'admin/', admin.site.urls),
url(r'^getinfos$',user_history),
url(r'^userinfos$',user_info),
url(r'^userauths$',user_auth),
url(r'^usertemplates$',user_templates),
url(r'listmycat',list_mycat),
url(r'^add_mycat$',add_mycat),
url(r'^add_mycat_monitor$',add_mycat_monitor),
url(r'^search_datasource$',search_datasource),
url(r'^create_dashboard$',create_dashboard),
url(r'^mycat_delete/(?P<id>[0-9]+)/$',views.mycat_delete,name='mycat_delete')
url(r'^addmycat',mycat_handler)
]
建議的方式是將各應用的URL配置在各自的urls.py中,這樣可以使應用更容易重複使用到不同項目裏:
urlpatterns = [
url(r'^admin/',admin.site.urls),
url(r'^xadmin/',xadmin.site.urls),
url(r'^DevOps/',include('scanhosts.urls',namespace='scanhosts')),
url(r'^DevOps/',include('grafahosts.urls',namespace='grafahosts')),
url(r'^DevOps/',include('dashboardhosts.urls',namespace='dashboardhosts'))
]
9、註釋規範:
# status:success/failed # data:object/string # errmsg:錯誤返回的信息,可以直接彈出給用戶 def mycat_del(request): """ :param:vip :return: 刪除成功,已刪除mycat的信息 """ errmsg = None status = "failed" context = { "status": status, "errmsg": errmsg } if request.method == 'POST': vip = request.POST.get('vip').strip() if vip is not None: try: Mycat.objects.filter(vip=vip).update(deleted=0) except vip.DoseNotExist: context['errmsg'] = "Mycat %s does not exist." % vip else: context["status"] = "success" return JsonResponse(context)
優美勝於醜陋(Python以編寫優美的代碼爲目標)
明瞭勝於晦澀(優美的代碼應當是明瞭的,命名規範,風格相似)
簡潔勝於複雜(優美的代碼應當是簡潔的,不要有複雜的內部實現)
複雜勝於凌亂(如果複雜不可避免,那代碼間也不能有難懂的關係,要保持接口簡潔)
扁平勝於嵌套(優美的代碼應當是扁平的,不能有太多的嵌套)
間隔勝於緊湊(優美的代碼有適當的間隔,不要奢望一行代碼解決問題)
可讀性很重要(優美的代碼是可讀的)