python 包及軟件開(kāi)發(fā)的目錄規(guī)范

一? 包介紹

隨著模塊數(shù)目的增多,把所有模塊不加區(qū)分地放到一起也是極不合理的,于是Python為我們提供了一種把模塊組織到一起的方法,即創(chuàng)建一個(gè)包。包就是一個(gè)含有__init__.py文件的文件夾,文件夾內(nèi)可以組織子模塊或子包,例如

pool/ #頂級(jí)包

├── __init__.py? ?

├── futures? ? ? ? ? #子包

│? ├── __init__.py

│? ├── process.py

│? └── thread.py

└── versions.py? ? ? #子模塊

需要強(qiáng)調(diào)的是

#1. 在python3中,即使包下沒(méi)有__init__.py文件,import 包仍然不會(huì)報(bào)錯(cuò),而在python2中,包下一定要有該文件,否則import 包報(bào)錯(cuò)

#2. 創(chuàng)建包的目的不是為了運(yùn)行,而是被導(dǎo)入使用,記住,包只是模塊的一種形式而已,包的本質(zhì)就是一種模

接下來(lái)我們就以包pool為例來(lái)介紹包的使用,包內(nèi)各文件內(nèi)容如下

# process.py

class ProcessPoolExecutor:

? ? def __init__(self,max_workers):

? ? ? ? self.max_workers=max_workers

? ? def submit(self):

? ? ? ? print('ProcessPool submit')

# thread.py

class ThreadPoolExecutor:

? ? def __init__(self, max_workers):

? ? ? ? self.max_workers = max_workers

? ? def submit(self):

? ? ? ? print('ThreadPool submit')

# versions.py

def check():

? ? print('check versions’)

# __init__.py文件內(nèi)容均為空

二? 包的使用

2.1 導(dǎo)入包與__init__.py

包屬于模塊的一種,因而包以及包內(nèi)的模塊均是用來(lái)被導(dǎo)入使用的,而絕非被直接執(zhí)行,首次導(dǎo)入包(如import pool)同樣會(huì)做三件事:

1、執(zhí)行包下的__init__.py文件

2、產(chǎn)生一個(gè)新的名稱空間用于存放_(tái)_init__.py執(zhí)行過(guò)程中產(chǎn)生的名字

3、在當(dāng)前執(zhí)行文件所在的名稱空間中得到一個(gè)名字pool,該名字指向__init__.py的名稱空間,例如http://pool.xxx和pool.yyy中的xxx和yyy都是來(lái)自于pool下的__init__.py,也就是說(shuō)導(dǎo)入包時(shí)并不會(huì)導(dǎo)入包下所有的子模塊與子包

import pool

pool.versions.check() #拋出異常AttributeError

pool.futures.process.ProcessPoolExecutor(3) #拋出異常AttributeError

pool.versions.check()要求pool下有名字versions,進(jìn)而pool.versions下有名字check。pool.versions下已經(jīng)有名字check了,所以問(wèn)題出在pool下沒(méi)有名字versions,這就需要在pool下的__init__.py中導(dǎo)入模塊versions

強(qiáng)調(diào)

1.關(guān)于包相關(guān)的導(dǎo)入語(yǔ)句也分為import和from ... import ...兩種,但是無(wú)論哪種,無(wú)論在什么位置,在導(dǎo)入時(shí)都必須遵循一個(gè)原則:凡是在導(dǎo)入時(shí)帶點(diǎn)的,點(diǎn)的左邊都必須是一個(gè)包,否則非法??梢詭в幸贿B串的點(diǎn),如import 頂級(jí)包.子包.子模塊,但都必須遵循這個(gè)原則。但對(duì)于導(dǎo)入后,在使用時(shí)就沒(méi)有這種限制了,點(diǎn)的左邊可以是包,模塊,函數(shù),類(它們都可以用點(diǎn)的方式調(diào)用自己的屬性)。

2、包A和包B下有同名模塊也不會(huì)沖突,如A.a與B.a來(lái)自倆個(gè)命名空間

3、import導(dǎo)入文件時(shí),產(chǎn)生名稱空間中的名字來(lái)源于文件,import 包,產(chǎn)生的名稱空間的名字同樣來(lái)源于文件,即包下的__init__.py,導(dǎo)入包本質(zhì)就是在導(dǎo)入該文件

2.2 絕對(duì)導(dǎo)入與相對(duì)導(dǎo)入

針對(duì)包內(nèi)的模塊之間互相導(dǎo)入,導(dǎo)入的方式有兩種

1、絕對(duì)導(dǎo)入:以頂級(jí)包為起始

#pool下的__init__.py

from pool import versions

2、相對(duì)導(dǎo)入:.代表當(dāng)前文件所在的目錄,..代表當(dāng)前目錄的上一級(jí)目錄,依此類推

#pool下的__init__.py

from . import versions

同理,針對(duì)pool.futures.process.ProcessPoolExecutor(3),則需要

#操作pool下的__init__.py,保證pool.futures

from . import futures #或from pool import futures

#操作futrues下的__init__.py,保證pool.futures.process

from . import process #或from pool.futures import process

在包內(nèi)使用相對(duì)導(dǎo)入還可以跨目錄導(dǎo)入模塊,比如thread.py中想引用versions.py的名字check

import也能使用絕對(duì)導(dǎo)入,導(dǎo)入過(guò)程中同樣會(huì)依次執(zhí)行包下的__init__.py,只是基于import導(dǎo)入的結(jié)果,使用時(shí)必須加上該前綴

例1:

import pool.futures #拿到名字pool.futures指向futures下的__init__.py

pool.futures.xxx #要求futures下的__init__.py中必須有名字xxx

例2:

import pool.futures.thread #拿到名字pool.futures.thread指向thread.py

thread_pool=pool.futures.thread.ThreadPoolExecutor(3)

thread_pool.submit()

相對(duì)導(dǎo)入只能用from module import symbol的形式,import ..versions語(yǔ)法是不對(duì)的,且symbol只能是一個(gè)明確的名字

from pool import futures.process #語(yǔ)法錯(cuò)誤

from pool.futures import process #語(yǔ)法正確

針對(duì)包內(nèi)部模塊之間的相互導(dǎo)入推薦使用相對(duì)導(dǎo)入,需要特別強(qiáng)調(diào):

1、相對(duì)導(dǎo)入只能在包內(nèi)部使用,用相對(duì)導(dǎo)入不同目錄下的模塊是非法的

2、無(wú)論是import還是from-import,但凡是在導(dǎo)入時(shí)帶點(diǎn)的,點(diǎn)的左邊必須是包,否則語(yǔ)法錯(cuò)誤

2.3 from 包 import *

? 在使用包時(shí)同樣支持from pool.futures import * ,毫無(wú)疑問(wèn)*代表的是futures下__init__.py中所有的名字,通用是用變量__all__來(lái)控制*代表的意思

#futures下的__init__.py

__all__=['process','thread']

? 最后說(shuō)明一點(diǎn),包內(nèi)部的目錄結(jié)構(gòu)通常是包的開(kāi)發(fā)者為了方便自己管理和維護(hù)代碼而創(chuàng)建的,這種目錄結(jié)構(gòu)對(duì)包的使用者往往是無(wú)用的,此時(shí)通過(guò)操作__init__.py可以“隱藏”包內(nèi)部的目錄結(jié)構(gòu),降低使用難度,比如想要讓使用者直接使用

import pool

pool.check()

pool.ProcessPoolExecutor(3)

pool.ThreadPoolExecutor(3)

需要操作pool下的__init__.py

from .versions import check

from .futures.process import ProcessPoolExecutor

from .futures.thread import ThreadPoolExecutor

三?軟件開(kāi)發(fā)目錄規(guī)范

為了提高程序的可讀性與可維護(hù)性,我們應(yīng)該為軟件設(shè)計(jì)良好的目錄結(jié)構(gòu),這與規(guī)范的編碼風(fēng)格同等重要。軟件的目錄規(guī)范并無(wú)硬性標(biāo)準(zhǔn),只要清晰可讀即可,假設(shè)你的軟件名為foo,筆者推薦目錄結(jié)構(gòu)如下

Foo/

|-- core/

|? |-- core.py

|

|-- api/

|? |-- api.py

|

|-- db/

|? |-- db_handle.py

|

|-- lib/

|? |-- common.py

|

|-- conf/

|? |-- settings.py

|

|-- run.py

|-- setup.py

|-- requirements.txt

|-- README

簡(jiǎn)要解釋一下:

? ? core/: 存放業(yè)務(wù)邏輯相關(guān)代碼

? ? api/: 存放接口文件,接口主要用于為業(yè)務(wù)邏輯提供數(shù)據(jù)操作。

? ? db/: 存放操作數(shù)據(jù)庫(kù)相關(guān)文件,主要用于與數(shù)據(jù)庫(kù)交互

? ? lib/: 存放程序中常用的自定義模塊

? ? conf/: 存放配置文件

? ? run.py: 程序的啟動(dòng)文件,一般放在項(xiàng)目的根目錄下,因?yàn)樵谶\(yùn)行時(shí)會(huì)默認(rèn)將運(yùn)行文件所在的文件夾作為sys.path的第一個(gè)路徑,這樣就省去了處理環(huán)境變量的步驟

? ? setup.py: 安裝、部署、打包的腳本。

? ? requirements.txt: 存放軟件依賴的外部Python包列表。

? ? README: 項(xiàng)目說(shuō)明文件。

除此之外,有一些方案給出了更加多的內(nèi)容,比如LICENSE.txt,ChangeLog.txt文件等,主要是在項(xiàng)目需要開(kāi)源時(shí)才會(huì)用到,請(qǐng)讀者自行查閱。

關(guān)于README的內(nèi)容,這個(gè)應(yīng)該是每個(gè)項(xiàng)目都應(yīng)該有的一個(gè)文件,目的是能簡(jiǎn)要描述該項(xiàng)目的信息,讓讀者快速了解這個(gè)項(xiàng)目。它需要說(shuō)明以下幾個(gè)事項(xiàng):

1、軟件定位,軟件的基本功能;

2、運(yùn)行代碼的方法: 安裝環(huán)境、啟動(dòng)命令等;

3、簡(jiǎn)要的使用說(shuō)明;

4、代碼目錄結(jié)構(gòu)說(shuō)明,更詳細(xì)點(diǎn)可以說(shuō)明軟件的基本原理;

5、常見(jiàn)問(wèn)題說(shuō)明。

關(guān)于setup.py和requirements.txt:

一般來(lái)說(shuō),用setup.py來(lái)管理代碼的打包、安裝、部署問(wèn)題。業(yè)界標(biāo)準(zhǔn)的寫(xiě)法是用Python流行的打包工具setuptools來(lái)管理這些事情,這種方式普遍應(yīng)用于開(kāi)源項(xiàng)目中。不過(guò)這里的核心思想不是用標(biāo)準(zhǔn)化的工具來(lái)解決這些問(wèn)題,而是說(shuō),一個(gè)項(xiàng)目一定要有一個(gè)安裝部署工具,能快速便捷的在一臺(tái)新機(jī)器上將環(huán)境裝好、代碼部署好和將程序運(yùn)行起來(lái)。

requirements.txt文件的存在是為了方便開(kāi)發(fā)者維護(hù)軟件的依賴庫(kù)。我們需要將開(kāi)發(fā)過(guò)程中依賴庫(kù)的信息添加進(jìn)該文件中,避免在 setup.py安裝依賴時(shí)漏掉軟件包,同時(shí)也方便了使用者明確項(xiàng)目引用了哪些Python包。

這個(gè)文件的格式是每一行包含一個(gè)包依賴的說(shuō)明,通常是flask>=0.10這種格式,要求是這個(gè)格式能被pip識(shí)別,這樣就可以簡(jiǎn)單的通過(guò) pip install -r requirements.txt來(lái)把所有Python依賴庫(kù)都裝好了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容