python編碼及工程規範

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以編寫優美的代碼爲目標)
明瞭勝於晦澀(優美的代碼應當是明瞭的,命名規範,風格相似)
簡潔勝於複雜(優美的代碼應當是簡潔的,不要有複雜的內部實現)
複雜勝於凌亂(如果複雜不可避免,那代碼間也不能有難懂的關係,要保持接口簡潔)
扁平勝於嵌套(優美的代碼應當是扁平的,不能有太多的嵌套)
間隔勝於緊湊(優美的代碼有適當的間隔,不要奢望一行代碼解決問題)
可讀性很重要(優美的代碼是可讀的)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章