理解 python 模塊加載和路徑查找

引用自理解 python 模塊加載和路徑查找

基礎(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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