? ? ? ? 一般項目中模塊一般會分布在文件系統(tǒng)的不同路徑中析苫。Python解釋器找到某個模塊并將其導(dǎo)入命名空間會遵循一定的搜索規(guī)則兜叨,這個規(guī)則就是模塊路徑搜索順序。
模塊路徑搜索
python搜索模塊的路徑順序:
1)衩侥、程序運行的工作目錄
2)国旷、PTYHONPATH目錄(如果已經(jīng)進行了設(shè)置)
3)、標(biāo)準(zhǔn)連接庫目錄(linux下一般在/usr/local/lib/python2.X/)
4)茫死、任何的.pth文件的內(nèi)容(如果存在的話).新功能跪但,允許用戶把有效果的目錄添加到模塊搜索路徑中去,.pth后綴的文本文件中一行一行的地列出目錄峦萎。
這四個組合起來就變成了sys.path了屡久。sys.path是python的搜索模塊的路徑集,是一個list爱榔,如
>>> importsys
>>>sys.path
['','/usr/lib64/python27.zip', '/usr/lib64/python2.7','/usr/lib64/python2.7/plat-linux2', '/usr/lib64/python2.7/lib-tk','/usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload','/root/.local/lib/python2.7/site-packages', '/usr/lib64/python2.7/site-packages','/usr/lib64/python2.7/site-packages/gtk-2.0','/usr/lib/python2.7/site-packages']
>>>?
? ? ? ? ? 因此只要模塊或者包所在的目錄在sys.path中被环,就可以使用import 模塊或import 包命令格式來使用。下面以如下一個文件結(jié)構(gòu)為例
[root@localhostpython]# tree
.
├──moduleA.py
├──mylib
│?? ├──moduleB_2.py
│?? ├──moduleB.py
│?? └──sublib
│????├──moduleC.py
├──otherlib
│?? ├──moduleD.py
└──run.py
同級目錄導(dǎo)入
? ? ? ? ?處于同級目錄的兩個模塊直接使用import?或from 模塊名 import *相互調(diào)用详幽。但這也只限于其中一個模塊文件作為主程序運行筛欢。如在run.py中下列導(dǎo)入是合法的導(dǎo)入
import moduleA
???? 而無法直接導(dǎo)入其他目錄中的模塊,如import moduleB是非法的唇聘。而且指定全路徑也是極其錯誤的版姑,如import?/test/python/mylib/moduleB,這和php不一樣迟郎。
??????? 而且導(dǎo)入過程和python解釋器當(dāng)前工作目錄無關(guān)漠酿,
[root@localhostpython]# python run.py
in moduleA file
in run file
[root@localhostpython]# cd ..
[root@localhosttest]# python python/run.py
in moduleA file
in run file
跨目錄調(diào)用
??? 跨目錄調(diào)用分以下幾種情況
[if !supportLists]????? [endif]主程序所在的目錄是模塊所在目錄的父(或祖輩)目錄
[if !supportLists]????? [endif]主程序?qū)肷蠈幽夸浿械哪K
跨目錄導(dǎo)入有統(tǒng)一的導(dǎo)入模式,可以使用sys.path.append方法將模塊加入到程序搜索路徑中谎亩。如在run.py中導(dǎo)入moduleB.py炒嘲,我們可以這么做
[root@localhostpython]# vi run.py
import sys
sys.path.append('/test/python/mylib')
?
import moduleB
print("in runfile")
再次運行
[root@localhostpython]# python run.py
in moduleB file
in run file
? ? ? 使用這種方法有兩個缺點宇姚,一是在退出python環(huán)境后自己添加的路徑就會自動消失了,且它是在實踐中極為脆弱夫凸,應(yīng)盡量避免使用纠亚;二是它將目錄名硬編碼到了你的源代碼昵骤。如果你的代碼被移到一個新的位置,這會導(dǎo)致維護問題。
? ? ? 為了解決這些問題甲捏,可以有以下兩個方法。
? ? ? ?使用PYTHONPATH環(huán)境變量來添加污呼。在自定義應(yīng)用程序中歧胁,這樣的環(huán)境變量可在程序啟動時設(shè)置或通過shell 腳本。如shell中桶现,
[root@localhost python]# export PYTHONPATH=/usr/lib/
[root@localhost python]# python
Python 2.7.5 (default, Apr? 92019, 14:30:50)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-36)] on linux2
Type "help", "copyright", "credits" or"license" for more information.
>>> import sys
>>> sys.path
['', '/usr/lib','/usr/lib64/python27.zip', '/usr/lib64/python2.7','/usr/lib64/python2.7/plat-linux2', '/usr/lib64/python2.7/lib-tk','/usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload','/root/.local/lib/python2.7/site-packages','/usr/lib64/python2.7/site-packages','/usr/lib64/python2.7/site-packages/gtk-2.0','/usr/lib/python2.7/site-packages']
>>>?
? ? ? ? 上述操作只針對當(dāng)前窗口生效躲雅,我們可以vi /etc/profile,在最后添加 export PYTHONPATH=/usr/lib/對所有用戶生效骡和。
? ? ? ? ?使用.pth文件相赁,在site-packages 文件中創(chuàng)建 .pth文件,將模塊的路徑寫進去慰于,一行一個路徑钮科。site-packages目錄是第三方包和模塊安裝的目錄。如果你手動安裝你的代碼婆赠,它將被安裝到site-packages目錄绵脯。雖然用于配置path的.pth 文件必須放置在site-packages里,但它配置的路徑可以是系統(tǒng)上任何你希望的目錄休里。因此蛆挫,你可以把你的代碼放在一系列不同的目錄,只要那些目錄包含在.pth 文件里份帐。
???????? 除了上述方法外璃吧,還可以將模塊封裝成包,見下節(jié)废境。
包
python中畜挨,每個py文件被稱之為模塊,而每個包含有__init__.py文件的目錄或文件夾被稱為包噩凹。在創(chuàng)建許許多多模塊后巴元,我們可能希望將某些功能相近的文件組織在同一文件夾下,在這個文件夾下再增加一個?__init__.py文件驮宴,這個文件夾就成為了包逮刨。即包是python模塊文件所在的目錄,且該目錄下必須存在__init__.py文件。典型的包結(jié)構(gòu)如下:
package
├── __init__.py
├── module_a.py
└── module_b.py
? ? ? ? ? 最簡單的情況下修己,只需要一個空的 __init__.py 文件即可恢总。當(dāng)然它也可以執(zhí)行包的初始化代碼或者設(shè)置__all__值。包下面是一些模塊文件和子目錄睬愤,假如子目錄中也有 __init__.py 那么它就是這個包的子包了片仿。
? ? ? ? 包的使用同模塊一樣,使用 import 尤辱、 from ... import 語句砂豌,來改變命名空間。
__init__.py
??如上所述光督,封裝成包是很簡單的阳距。在文件系統(tǒng)上組織你的代碼,并確保每個目錄都定義了一個__init__.py文件结借。
如在mylib文件夾下新建一個__init__文件筐摘。
[root@localhost mylib]# touch __init__.py
那么mylib目錄現(xiàn)在就是一個包。如果要在run.py中導(dǎo)入moduleB.py模塊映跟,可以寫入如下格式導(dǎo)入moduleB.py
[root@localhost python]# vi run.py
import mylib.moduleB
….
[root@localhost python]# python run.py
in moduleB file
in run file
即導(dǎo)入包中的模塊使用如下格式
pkg1.pkg2.~.pkgn.模塊名
包的導(dǎo)入注意點凡是在導(dǎo)入時帶點的蓄拣,點的左邊都必須是一個包扬虚,否則非法努隙。如果想要導(dǎo)入moduleC.py模塊,還需要在sublib目錄下新建__init__.py文件辜昵。如
[root@localhost sublib]# touch __init__.py
[root@localhost python]#vi run.py
import mylib.sublib.moduleC
…
[root@localhost python]# python run.py
in moduleC file
in run file
?
?????? 上述導(dǎo)入指明了包的全路徑荸镊,這個路徑(頂層包名)是相對于主程序所在目錄而言的,
[root@localhost mylib]# vi moduleB.py
import otherlib.moduleD?????????????#導(dǎo)入otherlib包
import moduleB_2??????????????????#同目錄下的模塊
print("in moduleB file")
?
[root@localhost mylib]# python moduleB.py
Traceback (most recent call last):
? File "moduleB.py",line 1, in
??? import otherlib.moduleD
ImportError: No module named otherlib.moduleD
這是因為moduleB.py所在的mylib目錄下沒有otherlib包堪置。
這也意味著我們在導(dǎo)入模塊的時候躬存,最好寫全包的路徑(從頂層包開始),除非兩個模塊在同一個目錄舀锨,如
[root@localhost python]# vi run.py
import mylib.moduleB?????????????????#導(dǎo)入mylib包
print("in run file")
?
[root@localhost python]# python run.py
in moduleD file
in moduleB_2 file
in moduleB file
in run file
?
?????? 它的導(dǎo)入過程是解釋器首先發(fā)現(xiàn)要導(dǎo)入mylib包下的moduleB.py岭洲,于是在該包下找到了moduleB.py,在導(dǎo)入該文件時坎匿,又發(fā)現(xiàn)需要導(dǎo)入otherlib包下的moduleD.py盾剩,于是去otherlib(就在當(dāng)前主程序同目錄下)包下找moduleD.py。
? ? ? ? 以上的用法都是使用了包的全路徑來導(dǎo)入的替蔬。這實際上算是一種絕對路徑告私。使用絕對路徑名的不利之處是這將頂層包名硬編碼到你的源碼中。如果你想重新組織它承桥,你的代碼將更脆驻粟,很難工作。舉個例子凶异,如果你改變了包名蜀撑,你就必須檢查所有文件來修正源碼挤巡。同樣,硬編碼的名稱會使移動代碼變得困難酷麦。
???而相對路徑中玄柏,用. 為當(dāng)前目錄,.. 為父目錄贴铜。但這種方法只適用于from … import的導(dǎo)入方式粪摘。如
[root@localhost python]# vi run.py
import mylib.sublib.moduleC
print("in run file")
[root@localhost sublib]# cat moduleC.py
from .. import moduleB_2
#import mylib.moduleB_2
print("in moduleC file")
?
上述語句即實現(xiàn)在sublib/module.py中導(dǎo)入上層目錄中的moduleB_2.py。
[root@localhost python]# python run.py
in moduleB_2 file
in moduleC file
in run file
注意Relative imports use a module’s name attribute to
determine that module’s position in the package hierarchy. Note that relative imports are based on the name of the current module.
? ? ? ?上述導(dǎo)入的過程是通過import mylib.sublib.moduleC找到moduleC.py文件绍坝,在該文件中徘意,因為有相對導(dǎo)入語句,就以當(dāng)前模塊為參考轩褐,去找父目錄下找moduleB_2.py椎咧,它實際上等價于import
mylib.moduleB_2。
top-levelscript
每一個被直接運行的python
script都會被視作是top-level script把介。top-level
script的__name__被自動設(shè)置成__main__勤讽。那么它所在的目錄將不被python視為包,通過相對導(dǎo)入將不起作用拗踢,如
[root@localhost mylib]# vi moduleB.py
from . import moduleB_2
print("in moduleB file")
?
[root@localhost mylib]# python moduleB.py
Traceback (most recent call last):
? File "moduleB.py",line 3, in
??? from . import moduleB_2
ValueError: Attempted relative import in non-package
?????? moduleB.py所在的目錄現(xiàn)在不是包了脚牍,通過相對導(dǎo)入將不起作用。
再比如
[root@localhost mylib]# vi moduleB.py
import sublib.moduleC
print("in moduleB file")
?
[root@localhost mylib]# cat sublib/moduleC.py
from .. import moduleB_2
print("in moduleC file")
?
[root@localhost mylib]# python moduleB.py
Traceback (most recent call last):
? File "moduleB.py",line 1, in
??? import sublib.moduleC
? File"/test/python/mylib/sublib/moduleC.py", line 1, in
??? from .. import moduleB_2
ValueError: Attempted relative import beyond toplevel package
? 這是因為moduleB.py作為主程序入口巢墅,mylib目錄就不是包了诸狭,而sublib就變成了頂層包。在sublib/moduleC.py中使用相對導(dǎo)入使用父目錄下的模塊當(dāng)然就不會被允許了君纫。