基礎(chǔ)概念
- module
模塊, 一個(gè) py 文件或以其他文件形式存在的可被導(dǎo)入的就是一個(gè)模塊 - package
包,包含有 init 文件的文件夾 - relative path
相對路徑,相對于某個(gè)目錄的路徑 - absolute path
絕對路徑,全路徑 - 路徑查找
python 解釋器查找被引入的包或模塊
python 執(zhí)行時(shí)是如何查找包和模塊的
執(zhí)行一個(gè) python 模塊
python 執(zhí)行一個(gè)文件,無論執(zhí)行的方式是絕對路徑還是相對路徑,解釋器都會(huì)把文件所在的目錄加入到系統(tǒng)查找路徑中,也就是sys.path
這個(gè)list
中,而 sys.path
又是由系統(tǒng)的 python
環(huán)境變量決定的。
#test.py
import os
import sys
print sys.path[0]
執(zhí)行這個(gè)文件:
python test.py
python /Users/x/workspace/blog-code/p2016_05_28_python_path_find
test.py
相對路徑和絕對路徑輸出相同的結(jié)果。test.py 所在的文件夾都會(huì)被加入 sys.path
的首位,注意這里是首位,也就是索引為0的位置。
解釋器執(zhí)行時(shí),首先搜索 built-in module
,也就是解釋器查找模塊最先查找的是built-in module
,其次才會(huì)搜索 sys.path
所包含的路徑。這樣的查找順序?qū)?huì)引起同名包或模塊被遮蔽的問題。
├── os.py
├── test2.py
├── redis.py
#test2.py
import os
from redis import Redis
執(zhí)行 test2.py 文件
Traceback (most recent call last):
File "/Users/x/workspace/blog-code/p2016_05_28_python_path_find/test2.py", line 1, in <module>
from redis import Redis
ImportError: cannot import name Redis
由于 os 是 built-in module ,即使在同目錄下有同名模塊,解釋器依然可以找到正確的os模塊,而 redis 屬于第三方模塊,默認(rèn)安裝位置是 python 環(huán)境變量中的 site-packages 下,解釋器啟動(dòng)之后會(huì)將此目錄加入 sys.path,按照上面所說的查找順序,優(yōu)先在執(zhí)行文件所在的目錄查找,由于其在 sys.path 的首位,因而本地的 redis 被導(dǎo)入。
交互式執(zhí)行環(huán)境
進(jìn)入交互式執(zhí)行環(huán)境,解釋器會(huì)自動(dòng)把當(dāng)前目錄加入 sys.path, 這時(shí)當(dāng)前目錄是以相對路徑的形式出現(xiàn)在 sys.path 中:
>>> import os.path
>>> import sys
>>> os.path.abspath(sys.path[0])
'/Users/x/workspace/blog-code'
>>>
除此之外,其他與執(zhí)行一個(gè)文件是相同的。
理解 file 變量
python doc: file is the pathname of the file from which the module was loaded, if it was loaded from a file. 如果一個(gè)模塊是從文件加載的,file 就是該模塊的路徑名。
顧名思義,當(dāng)模塊以文件的形式出現(xiàn) file 指的是模塊文件的路徑名,以相對路徑執(zhí)行 file 是相對路徑,以絕對路徑執(zhí)行 file 是絕對路徑。
#test.py
print __file__
相對路徑執(zhí)行
python test3.py
test3.py
絕對路徑執(zhí)行
python /Users/x/workspace/blog-code/p2016_05_28_python_path_find/test3.py
為了保證file 每次都能準(zhǔn)確得到模塊的正確位置,最好再取一次絕對路徑
os.path.abspath(__file__)
進(jìn)入交互式 shell ,輸入file 會(huì)報(bào)錯(cuò)誤
>>> __file__
Traceback (most recent call last):
File "<input>", line 1, in <module>
NameError: name '__file__' is not defined
這是因?yàn)楫?dāng)前交互式shell的執(zhí)行并不是以文件的形式加載,所以不存在__file__
這樣的屬性。
變量 sys.argv[0]
sys.argv[0]
是它用來獲取主入口執(zhí)行文件的變量。
A sample
#test.py
import sys
print __file__
print sys.argv[0]
以上倆個(gè)print輸出相同的結(jié)果,因?yàn)橹鲌?zhí)行文件和file所屬的模塊是同一個(gè)。當(dāng)我們改變?nèi)肟谖募瑓^(qū)別就出現(xiàn)了。
A sample
#test.py
import sys
print __file__
print sys.argv[0]
#test2.py
import test
執(zhí)行 test2.py
/Users/x/workspace/blog-code/p2016_05_28_python_path_find/child/test.py #__file__
test2.py #sys.argv[0]
總的來說,sys.argv[0]
是獲得入口執(zhí)行文件路徑,__file__
是獲得任意模塊文件的路徑。
sys.modules 的作用
既然python是在 sys.path 中搜索模塊的,那載入的模塊存放在何處?答案就是 sys.modules,是存放已載入模塊的地方。模塊一經(jīng)載入,python 會(huì)把這個(gè)模塊加入 sys.modules 中供下次載入使用,這樣可以加速模塊的引入,起到緩存的作用。
>>> import sys
>>> sys.modules['tornado']
Traceback (most recent call last):
File "<input>", line 1, in <module>
KeyError: 'tornado'
>>> import tornado
>>> sys.modules['tornado']
<module 'tornado' from '/Users/x/python_dev/lib/python2.7/site-packages/tornado/__init__.pyc'>
前面說過python 解釋器啟動(dòng)之后,會(huì)把預(yù)先載入 built-in module,可以通過 sys.modules 驗(yàn)證。
>>> sys.modules['os']
<module 'os' from '/Users/x/python_dev/lib/python2.7/os.pyc'>
>>>
獲取模塊的路徑
借助 sys.modules和file, 可以動(dòng)態(tài)獲取所有已加載模塊目錄和路徑:
>>> import os
>>> os.path.realpath(sys.modules['os'].__file__)
'/Users/x/python_dev/lib/python2.7/os.pyc'
>>> import tornado
>>> os.path.realpath(sys.modules['tornado'].__file__)
'/Users/x/python_dev/lib/python2.7/site-packages/tornado/__init__.pyc'
def get_module_dir(name):
path = getattr(sys.modules[name], '__file__', None)
if not path
raise AttributeError('module %s has not attribute __file__'%name)
return os.path.dirname(os.path.abspath(path))
from 和 import 語句
知道了 python 是如何搜索模塊和保存已導(dǎo)入模塊,我們再說說 python 模塊的導(dǎo)入
導(dǎo)入順序
python 包的導(dǎo)入順序是
系統(tǒng)包 --> 同目錄 -- > sys.path
因此同名的包,系統(tǒng)包優(yōu)先級最高 > 同目錄 > sys.path,之所以有這樣的差別是因?yàn)楫?dāng)前執(zhí)行目錄會(huì)優(yōu)先加入sys.path 的首位。
導(dǎo)入時(shí)執(zhí)行
當(dāng)導(dǎo)入一個(gè)模塊時(shí),模塊的頂層變量會(huì)被執(zhí)行,包括全局變量,類以及函數(shù)的聲明,因此在編寫模塊時(shí)一定要注意消除副作用(函數(shù)的調(diào)用)。
import 語句 和 from 語句
能被 import 的包括:package,package 中的模塊,模塊中的變量。影響 import 的屬性是__all__
, __all__
是個(gè)list,能夠影響被 package 中 以 from package import *
被導(dǎo)出的模塊,即定義在__all__
中的模塊才會(huì)被 from package import *
導(dǎo)出。
summary
python 的模塊導(dǎo)入,加載和查找還有很多可以說說的地方,尤其是動(dòng)態(tài) import, 對應(yīng)python中的關(guān)鍵字是 import,感興趣的同學(xué)可以研究一下 tornado.util
模塊下的 import_object
。