python工程結(jié)構(gòu)

在一個健康的開發(fā)周期中链韭,代碼風(fēng)格拦英,API設(shè)計(jì)和自動化是非常關(guān)鍵的心剥。同樣的,對于工程的架構(gòu) ,倉庫的結(jié)構(gòu)也是關(guān)鍵的一部分嚎莉。
當(dāng)一個潛在的用戶和貢獻(xiàn)者登錄到您的倉庫頁面時,他們會看到這些:

  • 工程的名字
  • 工程的描述
  • 一系列的文件

如果您的倉庫的目錄是一團(tuán)糟沛豌,沒有清晰的結(jié)構(gòu)萝喘,他們可能要到處尋找才能找到您寫的漂亮的文檔。

倉庫樣例

README.rst
LICENSE
setup.py
requirements.txt
sample/__init__.py
sample/core.py
sample/helpers.py
docs/conf.py
docs/index.rst
tests/test_basic.py
tests/test_advanced.py

以下是一些細(xì)節(jié)介紹:

  • 核心代碼
    布局:./sample/ or ./sample.py
    您的模塊包是這個倉庫的核心琼懊,它不應(yīng)該隱藏起來:
    ./sample/
    如果您的模塊只有一個文件,那么您可以直接將這個文件放在倉庫的根目錄下:
    ./sample.py
    這個模塊文件不應(yīng)該屬于任何一個模棱兩可的src或者python子目錄爬早。
  • License
    作用:許可證哼丈,在這個文件中要有完整的許可說明和授權(quán)。
  • Setup.py
    作用:打包和發(fā)布管理
  • requirements.txt
    作用:開發(fā)依賴
    說明: requirements.txt應(yīng)該放在倉庫的根目錄筛严。它應(yīng)該指明完整工程的所有依賴包: 測試, 編譯和文檔生成醉旦。
    如果您的工程沒有任何開發(fā)依賴,或者您喜歡通過 setup.py 來設(shè)置桨啃,那么這個文件不是必須的车胡。
  • Documentsation
    作用:包的參考文檔
  • Test Suite
    作用:包的集合和單元測試
    最開始,一組測試?yán)又皇欠旁谝粋€文件當(dāng)中:
    ./test_sample.py
    當(dāng)測試?yán)又鸩皆黾訒r照瘾,您會把它放到一個目錄里面匈棘,像下面這樣:
tests/test_basic.py
tests/test_advanced.py

當(dāng)然,這些測試?yán)有枰獙?dǎo)入我們的包來進(jìn)行測試析命,有幾種方式來處理:
1.將我們的包安裝到site-packages中主卫。
2.通過簡單直接的路徑設(shè)置來解決導(dǎo)入的問題。
推薦后者鹃愤。如果使用 setup.py develop 來測試一個持續(xù)更新的代碼庫簇搅,需要為每一個版本的代碼庫設(shè)置一個獨(dú)立的測試環(huán)境.太麻煩了。
可以先創(chuàng)建一個包含上下文環(huán)境的文件 tests/context.py软吐。 file:

import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample

然后瘩将,在每一個測試文件中,導(dǎo)入:
from .context import sample
這樣就能夠像期待的那樣工作,而不用采用安裝的方式姿现。

  • Makefile
    作用:常規(guī)的管理任務(wù)
    ** 樣例 Makefile:**
init:
    pip install -r requirements.txt
test:
    py.test tests
PHONY: init test

一些其他的常規(guī)管理腳本(比如 manage.py 或者 fabfile.py)肠仪,也放在倉庫的根目錄下.

結(jié)構(gòu)是一把鑰匙

得益于Python提供的導(dǎo)入與管理模塊的方式,結(jié)構(gòu)化Python項(xiàng)目變得相對簡單建钥。 這里說的簡單藤韵,指的是結(jié)構(gòu)化過程沒有太多約束限制而且模塊導(dǎo)入功能容易掌握。 因而您只剩下架構(gòu)性的工作熊经,包括設(shè)計(jì)泽艘、實(shí)現(xiàn)項(xiàng)目各個模塊,并整理清他們之間 的交互關(guān)系镐依。

模塊

Python模塊是最主要的抽象層之一匹涮,并且很可能是最自然的一個。抽象層允許將代碼分為 不同部分槐壳,每個部分包含相關(guān)的數(shù)據(jù)與功能然低。請盡量保持模塊名稱簡單,以無需分開單詞务唐。 最重要的是雳攘,不要使用下劃線命名空間,而是使用子模塊枫笛。

# ok
import library.plugin.foo
# not OK
import library.foo_plugin
  • 理解import的原理機(jī)制
    具體來說吨灭,import modu 語句將 尋找合適的文件,即調(diào)用目錄下的 modu.py 文件(如果該文件存在)刑巧。如果沒有 找到這份文件喧兄,Python解釋器遞歸地在 "PYTHONPATH" 環(huán)境變量中查找該文件,如果仍沒 有找到啊楚,將拋出ImportError異常吠冤。
    一旦找到 modu.py,Python解釋器將在隔離的作用域內(nèi)執(zhí)行這個模塊恭理。所有頂層 語句都會被執(zhí)行拯辙,包括其他的引用。方法與類的定義將會存儲到模塊的字典中颜价。然后薄风,這個 模塊的變量、方法和類通過命名空間暴露給調(diào)用方拍嵌,這是Python中特別有用和強(qiáng)大的核心概念遭赂。
    在很多其他語言中,include file 指令被預(yù)處理器用來獲取文件里的所有代碼并‘復(fù)制’ 到調(diào)用方的代碼中横辆。Python則不一樣:include代碼被獨(dú)立放在模塊命名空間里撇他,這意味著您 一般不需要擔(dān)心include的代碼可能造成不好的影響茄猫,例如重載同名方法。
    也可以使用import語句的特殊形式 from modu import *模擬更標(biāo)準(zhǔn)的行為困肩。但 import * 通常 被認(rèn)為是不好的做法划纽。使用 from modu import * 的代碼較難閱讀而且依賴獨(dú)立性不足。 使用 from modu import func能精確定位您想導(dǎo)入的方法并將其放到全局命名空間中锌畸。 比 from modu import * 要好些勇劣,因?yàn)樗鞔_地指明往全局命名空間中導(dǎo)入了什么方法,它和 import modu 相比唯一的優(yōu)點(diǎn)是之后使用方法時可以少打點(diǎn)兒字潭枣。

from modu import *
x = sqrt(4)  # sqrt是模塊modu的一部分么比默?或是內(nèi)建函數(shù)么?上文定義了么盆犁?

稍好

from modu import sqrt
x = sqrt(4)  # 如果在import語句與這條語句之間命咐,sqrt沒有被重復(fù)定義,它也許是模塊modu的一部分谐岁。

最好

import modu
[...]
x = modu.sqrt(4)  # sqrt顯然是屬于模塊modu的醋奠。

除了簡單的單文件項(xiàng)目外,其他項(xiàng)目需要能夠明確指出類和方法 的出處伊佃,例如使用 modu.func 語句窜司,這將顯著提升代碼的可讀性和易理解性。

Python提供非常簡單的包管理系統(tǒng)航揉,即簡單地將模塊管理機(jī)制擴(kuò)展到一個目錄上(目錄擴(kuò)展為包)塞祈。
任意包含 init.py 文件的目錄都被認(rèn)為是一個Python包。導(dǎo)入一個包里不同模塊的方式和普通的導(dǎo)入模塊方式相似迷捧,特別的地方是 init.py 文件將集合所有包范圍內(nèi)的定義。

pack/目錄下的modu.py文件通過 import pack.modu語句導(dǎo)入胀葱。 該語句會在 pack 目錄下尋找 init.py 文件漠秋,并執(zhí)行其中所有頂層語句。以上操作之后抵屿,modu.py 內(nèi)定義的所有變量庆锦、方法和類在pack.modu命名空間中均可看到。

一個常見的問題是往 init.py 中加了過多代碼轧葛,隨著項(xiàng)目的復(fù)雜度增長搂抒, 目錄結(jié)構(gòu)越來越深,子包和更深嵌套的子包可能會出現(xiàn)尿扯。在這種情況下求晶,導(dǎo)入多層嵌套 的子包中的某個部件需要執(zhí)行所有通過路徑里碰到的 init.py文件。如果包內(nèi)的模塊和子包沒有代碼共享的需求衷笋,使用空白的 init.py 文件是正常甚至好的做法芳杏。

最后,導(dǎo)入深層嵌套的包可用這個方便的語法:import very.deep.module as mod。 該語法允許使用 mod 替代冗長的 very.deep.module爵赵。

面向?qū)ο缶幊?/h2>

在Python中一切都是對象吝秕,并且能按對象的方式處理。這么說的意思是空幻,例如函數(shù)是一等對象烁峭。 函數(shù)、類秕铛、字符串乃至類型都是Python對象:與其他對象一樣约郁,他們有類型,能作為函數(shù)參數(shù)傳遞如捅,并且還可能有自己的方法和屬性棍现。這樣理解的話,Python是一種面向?qū)ο笳Z言镜遣。
然而己肮,與Java不同的是,Python并沒有將面向?qū)ο缶幊套鳛樽钪饕木幊谭妒奖亍7敲嫦驅(qū)ο蟮腜ython項(xiàng)目(比如谎僻,使用較少甚至不使用類定義,類繼承寓辱,或其它面向?qū)ο缶幊痰臋C(jī)制)也是完全可行的艘绍。
在一些情況下,需要避免不必要的面向?qū)ο箫ぁ.?dāng)我們想要將狀態(tài)與功能結(jié)合起來诱鞠,使用標(biāo)準(zhǔn)類定義是有效的。但正如函數(shù)式編程所討論的那個問題这敬,函數(shù)式的“變量”狀態(tài)與類的狀態(tài)并不相同航夺。

動態(tài)類型

Python是動態(tài)類型語言,這意味著變量并沒有固定的類型崔涂。實(shí)際上阳掐,Python中的變量和其他語言有很大的不同,特別是靜態(tài)類型語言冷蚂。變量并不是計(jì)算機(jī)內(nèi)存中被寫入的某個值缭保,它們只是指向內(nèi)存的 ‘標(biāo)簽’ 或 ‘名稱’ 。因此可能存在這樣的情況蝙茶,變量 'a' 先代表值1艺骂,然后變成字符串'a string' , 然后又變?yōu)橹赶蛞粋€函數(shù)。

Python 的動態(tài)類型常被認(rèn)為是它的缺點(diǎn)隆夯,的確這個特性會導(dǎo)致復(fù)雜度提升和難以調(diào)試的代碼彻亲。 命名為 'a' 的變量可能是各種類型孕锄,開發(fā)人員或維護(hù)人員需要在代碼中追蹤命名,以保證它 沒有被設(shè)置到毫不相關(guān)的對象上苞尝。

避免發(fā)生類似問題的參考方法:

  • 避免對不同類型的對象使用同一個變量名
a = 1
a = 'a string'
def a():
    pass  # 實(shí)現(xiàn)代碼

count = 1
msg = 'a string'
def func():
    pass  # 實(shí)現(xiàn)代碼

使用簡短的函數(shù)或方法能降低對不相關(guān)對象使用同一個名稱的風(fēng)險(xiǎn)畸肆。即使是相關(guān)的不同 類型的對象,也更建議使用不同命名

重復(fù)使用命名對效率并沒有提升:賦值時無論如何都要創(chuàng)建新的對象宙址。然而隨著復(fù)雜度的 提升轴脐,賦值語句被其他代碼包括 'if' 分支和循環(huán)分開,使得更難查明指定變量的類型抡砂。 在某些代碼的做法中大咱,例如函數(shù)編程,推薦的是從不重復(fù)對同一個變量命名賦值注益。Java 內(nèi)的實(shí)現(xiàn)方式是使用 'final' 關(guān)鍵字碴巾。Python并沒有 'final' 關(guān)鍵字。盡管如此丑搔,避免給同一個變量命名重復(fù)賦值仍是是個好的做法厦瓢,并且有助于掌握 可變與不可變類型的概念。

可變和不可變類型

Python提供兩種內(nèi)置或用戶定義的類型啤月≈蟪穑可變類型允許內(nèi)容的內(nèi)部修改。典型的動態(tài)類型 包括列表與字典:列表都有可變方法谎仲,如 list.append() 和 list.pop()浙垫, 并且能就地修改。字典也是一樣郑诺。不可變類型沒有修改自身內(nèi)容的方法夹姥。比如,賦值為整數(shù) 6的變量 x 并沒有 "自增" 方法辙诞,如果需要計(jì)算 x + 1辙售,必須創(chuàng)建另一個整數(shù)變量并給其命名。

my_list = [1, 2, 3]
my_list[0] = 4
print my_list  # [4, 2, 3] <- 原列表改變了

x = 6
x = x + 1  # x 變量是一個新的變量

這種差異導(dǎo)致的一個后果就是倘要,可變類型是不 '穩(wěn)定 '的圾亏,因而不能作為字典的鍵使用十拣。合理地 使用可變類型與不可變類型有助于闡明代碼的意圖封拧。例如與列表相似的不可變類型是元組, 創(chuàng)建方式為 (1, 2)夭问。元組是不可修改的泽西,并能作為字典的鍵使用。

Python 中一個可能會讓初學(xué)者驚訝的特性是:字符串是不可變類型缰趋。這意味著當(dāng)需要組合一個字符串時捧杉,將每一部分放到一個可變列表里陕见,使用字符串時再組合 ('join') 起來的做法更高效。 而且味抖,使用列表推導(dǎo)的構(gòu)造方式比在循環(huán)中調(diào)用append()來構(gòu)造列表更好也更快评甜。

# 創(chuàng)建將0到19連接起來的字符串 (例 "012..1819")
nums = ""
for n in range(20):
    nums += str(n)   # 慢且低效
print nums

# 創(chuàng)建將0到19連接起來的字符串 (例 "012..1819")
nums = []
for n in range(20):
    nums.append(str(n))
print "".join(nums)  # 更高效

更好

# 創(chuàng)建將0到19連接起來的字符串 (例 "012..1819")
nums = [str(n) for n in range(20)]
print "".join(nums)

最好Best

# 創(chuàng)建將0到19連接起來的字符串 (例 "012..1819")
nums = map(str, range(20))
print "".join(nums)

除了 str.join() 和 +,也可以使用 % 格式運(yùn)算符來連接確定數(shù)量的字符串仔涩,不過PEP 3101 建議使用 str.format() 替代 % 操作符忍坷。

foo = 'foo'
bar = 'bar'

foobar = '%s%s' % (foo, bar) # 可行
foobar = '{0}{1}'.format(foo, bar) # 更好
foobar = '{foo}{bar}'.format(foo=foo, bar=bar) # 最好
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市熔脂,隨后出現(xiàn)的幾起案子佩研,更是在濱河造成了極大的恐慌,老刑警劉巖霞揉,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旬薯,死亡現(xiàn)場離奇詭異,居然都是意外死亡适秩,警方通過查閱死者的電腦和手機(jī)绊序,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來隶症,“玉大人政模,你說我怎么就攤上這事÷旎幔” “怎么了淋样?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長胁住。 經(jīng)常有香客問我趁猴,道長,這世上最難降的妖魔是什么彪见? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任儡司,我火速辦了婚禮,結(jié)果婚禮上余指,老公的妹妹穿的比我還像新娘捕犬。我一直安慰自己,他們只是感情好酵镜,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布碉碉。 她就那樣靜靜地躺著,像睡著了一般淮韭。 火紅的嫁衣襯著肌膚如雪垢粮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天靠粪,我揣著相機(jī)與錄音蜡吧,去河邊找鬼毫蚓。 笑死,一個胖子當(dāng)著我的面吹牛昔善,可吹牛的內(nèi)容都是我干的元潘。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼君仆,長吁一口氣:“原來是場噩夢啊……” “哼柬批!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起袖订,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤氮帐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后洛姑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體上沐,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年楞艾,在試婚紗的時候發(fā)現(xiàn)自己被綠了参咙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡硫眯,死狀恐怖蕴侧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情两入,我是刑警寧澤净宵,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站裹纳,受9級特大地震影響择葡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜剃氧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一敏储、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧朋鞍,春花似錦已添、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至恨狈,卻和暖如春疏哗,著一層夾襖步出監(jiān)牢的瞬間呛讲,已是汗流浹背禾怠。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工返奉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吗氏。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓芽偏,卻偏偏與公主長得像,于是被迫代替她去往敵國和親弦讽。 傳聞我的和親對象是個殘疾皇子污尉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 一、模塊 1往产、模塊和導(dǎo)入 當(dāng)程序代碼量變得相當(dāng)大被碗、邏輯結(jié)構(gòu)變得非常復(fù)雜的時候,我們最好把代碼按照邏輯和功能劃分成一...
    常大鵬閱讀 2,970評論 0 9
  • 模塊和包 一 模塊 1 什么是模塊? 常見的場景:一個模塊就是一個包含了python定義和聲明的文件蔼囊,文件名就是...
    go以恒閱讀 2,261評論 0 4
  • IO密集型程序焚志、深拷貝和淺拷貝、模塊導(dǎo)入畏鼓、with 語句 1.1 GIL 學(xué)習(xí)目標(biāo) 1. 能夠說出 GIL 是什...
    Cestine閱讀 745評論 0 0
  • 定義類并創(chuàng)建實(shí)例 在Python中酱酬,類通過 class 關(guān)鍵字定義。以 Person 為例云矫,定義一個Person類...
    績重KF閱讀 3,924評論 0 13
  • 兒子膳沽,今天這篇文章沒有跌宕起伏,沒有華麗辭藻让禀,只是用來記錄你人生當(dāng)中最重要的時刻贵少。感謝在你十二年求學(xué)生涯,給予你關(guān)...
    毛桐閱讀 1,645評論 16 21