1. 程式人生 > >深入理解Python解析器對模組位置的搜尋順序

深入理解Python解析器對模組位置的搜尋順序

最初接觸python時通過http://www.runoob.com/python/python-modules.html的教程學習python的基礎知識。

看模組和包時看到這麼一段話:

定位模組

當你匯入一個模組,Python解析器對模組位置的搜尋順序是:

  • 當前目錄
  • 如果不在當前目錄,Python 則搜尋在 shell 變數 PYTHONPATH 下的每個目錄。
  • 如果都找不到,Python會察看預設路徑。UNIX下,預設路徑一般為/usr/local/lib/python/。

模組搜尋路徑儲存在system模組的sys.path變數中。變數裡包含當前目錄,PYTHONPATH和由安裝過程決定的預設目錄。

後面兩條都沒啥問題,當前目錄這句話卻讓我理解為凡是兩個python指令碼在同一目錄下,一定可以直接互相import,但是實際敲程式碼試的時候發現不是這回事。

依據上段話內容程式碼舉例引出問題:

ubuntu/python3.5.2環境下,在/home/xxx/workspace目錄下建立資料夾package。

package目錄下建立指令碼print.py,寫入如下程式碼:

1 def print_text(x):
2     print(x)

繼續建立指令碼__init__.py,寫入如下程式碼:

1 from print import print_text

在workspace目錄下建立指令碼main.py,寫入如下程式碼:

1 import package
2 package.print_text('Hello world!')

最終檔案結構如下:

-/home/xxx/workspace/
    -main.py
    -package
        -__init__.py
        -print.py

按照之前錯誤的理解執行main.py會在終端輸出hello word,實際上在終端中cd到/home/xxx/workspace/目錄,python3 main.py執行main.py後會得到如下報錯資訊:

Traceback (most recent call last):
  File "/home/chen/workspace/main.py", line 1, in <module>
    import package
  File "/home/chen/workspace/package/__init__.py", line 1, in <module>
    from print import print_text
ImportError: No module named 'print'

即雖然__init__.py和print.py在同一級目錄下,但是__init__.py無法搜尋到print.py所在目錄。

在__init__.py程式碼中新增列印系統環境變數的程式碼如下:

1 import sys
2 # 列印系統path變數
3 print(sys.path)
4 from print import print_text

重新執行main.py,輸出資訊變為

['/home/xxx/workspace/', '/usr/local/lib/python3.5/dist-packages/setuptools-18.1-py3.5.egg', '/usr/local/lib/python3.5/dist-packages/pip-7.1.0-py3.5.egg', '/usr/local/lib/python3.5/dist-packages/howdoi-1.1.9-py3.5.egg', '/usr/local/lib/python3.5/dist-packages/requests_cache-0.4.12-py3.5.egg', '/usr/local/lib/python3.5/dist-packages/Pygments-2.1.3-py3.5.egg', '/usr/local/lib/python3.5/dist-packages/pyquery-1.2.17-py3.5.egg', '/usr/local/lib/python3.5/dist-packages/cssselect-1.0.0-py3.5.egg', '/usr/lib/python3/dist-packages', '/usr/local/lib/python3.5/dist-packages/tornado-4.4.2-py3.5-linux-x86_64.egg', '/usr/lib/python35.zip', '/usr/lib/python3.5', '/usr/lib/python3.5/plat-x86_64-linux-gnu', '/usr/lib/python3.5/lib-dynload', '/usr/local/lib/python3.5/dist-packages']
Traceback (most recent call last):
  File "/home/xxx/workspace/main.py", line 1, in <module>
    import package
  File "/home/workspace/package/__init__.py", line 3, in <module>
    from print import print_text
ImportError: No module named 'print'

可以看到__init__.py所在目錄/home/xxx/workspace/package/確實不在系統環境變數中,取而代之的則是main.py所在的目錄。

那麼是否當前目錄實際上僅僅指當前所執行的程式的所在目錄呢?如果成立,將__init__.py程式碼修改為如下一定可以正常輸出hello world,試下

1 from package.print import print_text

重新執行main.py,果然成功輸出Hello world!

繼續驗證,如果上面猜測成立,還一定能得出直接執行__init__.py不會報錯的結論,試下,__init__.py的程式碼改為

1 import sys
2 print(sys.path)
3 from print import print_text
4 print_text('Hello world!')

執行main.py,輸出如下:

['/home/xxx/workspace/package', '/usr/local/lib/python3.5/dist-packages/setuptools-18.1-py3.5.egg', '/usr/local/lib/python3.5/dist-packages/pip-7.1.0-py3.5.egg', '/usr/local/lib/python3.5/dist-packages/howdoi-1.1.9-py3.5.egg', '/usr/local/lib/python3.5/dist-packages/requests_cache-0.4.12-py3.5.egg', '/usr/local/lib/python3.5/dist-packages/Pygments-2.1.3-py3.5.egg', '/usr/local/lib/python3.5/dist-packages/pyquery-1.2.17-py3.5.egg', '/usr/local/lib/python3.5/dist-packages/cssselect-1.0.0-py3.5.egg', '/usr/lib/python3/dist-packages', '/usr/local/lib/python3.5/dist-packages/tornado-4.4.2-py3.5-linux-x86_64.egg', '/home/xxx/workspace/package', '/usr/lib/python35.zip', '/usr/lib/python3.5', '/usr/lib/python3.5/plat-x86_64-linux-gnu', '/usr/lib/python3.5/lib-dynload', '/usr/local/lib/python3.5/dist-packages']
Hello world!

可以看到,環境變數列印結果中確實有一條路徑已經變為__init__.py所在的目錄,並且hello world列印成功!

至此得出結論:當前目錄指當前執行程式的所在目錄。

所以在import非程式入口的同級目錄下其它python指令碼時,我們有以下兩種可行的做法

1.引入模組的路徑寫為相對於入口程式所在路徑的相對路徑,例如上面例子__init__.py寫為

1 from package.print import print_text

2.在指令碼頭部將當前檔案的所在目錄直接插入直譯器搜尋的系統path中,例如將__init__.py改寫為

1 import sys
2 sys.path.append('/home/xxx/workspace/package/')
3 from print import print_text

相關推薦

深入理解Python解析模組位置搜尋順序

最初接觸python時通過http://www.runoob.com/python/python-modules.html的教程學習python的基礎知識。 看模組和包時看到這麼一段話: 定位模組 當你匯入一個模組,Python解析器對模組位置的搜尋順序是: 當前

深入理解Python 裝飾(decorator)

返璞歸真, 看山還是山 剛看到Python裝飾器時, 覺得很神奇。簡單實驗下,發現也就那麼回事。但是慢慢的看到越來越多的裝飾器。很多時候又不瞭解到底是怎麼回事了。 最後還是決定好好研究下。 先看看一些例項, 然後再來分析下原理 假設我們有如下

python 裝飾深入理解python裝飾

要想徹底搞懂Python中的裝飾器,除了需要有一點Python中的函式基礎,還需要解決如下四個問題。當我們解決了這四個問題後,也就徹底搞懂Python中的裝飾器。 1.什麼是裝飾器,其本質是什麼? 2.裝飾器有什麼作用? 3.裝飾器有什麼使用特點(使用原則)?

深入理解python 命令列解析模組optparse(optparse原始碼解讀)

optparse是python用來解析命令列引數的,最早是getopt,option比getopt更強大和靈活。最新的命令列解析使用argparse,因此optparse今後將不會再被開發,optparse在python的原始碼位置是Lib/optparse

完全理解Python叠代象、叠代、生成器

語句 優雅 能力 技術分享 其它 ice start ssi eth 在了解Python的數據結構時,容器(container)、可叠代對象(iterable)、叠代器(iterator)、生成器(generator)、列表/集合/字典推導式(list,set,dict c

深入理解python裏面類的象的賦值

技術 技術分享 pytho sel sed 深入理解 ID alt code class T(): def __init__(self): self.name=324 pass a=T() a.name=999 b=a #深入理解類,類裏

深入理解python的生成器表示式和列表解析

前言       沒有用過的東西,沒有深刻理解的東西很難說自己會,而且被別人一問必然破綻百出。雖然之前有接觸過python協程的概念,但是隻是走馬觀花,這兩天的一次交談中,別人問到了協程,頓時語塞,死活想不起來曾經看過的東西,之後突然想到了yield,但為時已晚,只能說

Redis的深入理解解析

最近換了專案中,突然碰到redis了,對他的理解很有限,這裡做個分享。1.Redis的連線池(JedisPool)說到連線池,我的第一反應是這可能跟資料來源的C3P0類似。結果一查,恩,差不多。Redis是一種C/S的模式,但是頻繁的連結會導致花費在底層連結上的時間大大增加(

深入理解 Python 中的裝飾

function 問題 深入 工程 嵌套 tag 不同 class 完全   裝飾器本質上也是函數,接收函數對象來作為參數,並在裝飾器的內部來調用接受的函數對象完成相關的函數調用,也可以這樣理解 ,為了方便在幾個不同函數調用之前或者完成相關的統一操作,註意是完成統一的操

深入理解python(一)python語法總結:基礎知識和python中物件的理解

用python也用了兩年了,趁這次疫情想好好整理下。 大概想法是先對python一些知識點進行總結,之後就是根據python核心原始碼來對python的實現方式進行學習,不會閱讀整個原始碼,,,但是應該會把資料結構的實現、函式呼叫過程、以及python虛擬機器的基本原理根據原始碼解釋下。 當然限於筆者只是一個

深入理解 Python yield

http RR recent 當我 IE error -a 沒有 因此 https://blog.csdn.net/lftaoyuan/article/details/78915518 python2和python3是不兼容的,通篇環境都是python3.6 簡單的yi

如何理解python裝飾

() 如何 lee 簡單的 存在 port print pytho -s 如何理解裝飾器python 學習遇到的第一個難點是裝飾器。裝飾器的作用是不大規模改動代碼的情況下,增加功能。作用:為已經存在的對象添加額外的功能特點:不需要對對象做任何的代碼上的變動。以一個例子來講裝

Python解析

部分 .html https diff bsp java字節碼 解釋器 擴展名 wikipedia 當我們編寫Python代碼時,我們得到的是一個包含Python代碼的以.py為擴展名的文本文件。要運行代碼,就需要Python解釋器去執行.py文件。 由於整個Python語

深入理解python之二——python列表和元組

n) 數據 兩種 性能 執行 效率 動態 單元 這一 從一開始學習python的時候,很多人就聽到的是元組和列表差不多,區別就是元組不可以改變,列表可以改變。 從數據結構來說,這兩者都應當屬於數組,元組屬於靜態的數組,而列表屬於動態數組。稍後再內存的分配上也會體現這一點。對

深入理解CSS選擇優先級

ring and tor 而不是 問題 ack 是不是 http important 題外話 今天把 《CSS REFACTORING》(中文名叫《CSS重構:樣式表性能調優》)電子書粗略的瀏覽了一遍,這本書很薄,150頁左右,首先是介紹了什麽是重構並舉了兩個簡單的重構例子

深入理解CSS選擇優先順序

什麼是選擇器優先順序(Specificity) 直接複製了 MDN對優先順序的定義 上的解釋: 瀏覽器通過優先順序來判斷哪一些屬性值與一個元素最為相關,從而在該元素上應用這些屬性值。優先順序是基於不同種類選擇器組成的匹配規則。 這句話也是很抽象,暫且先不管它了。但是我們可以先看一個例子:

深入理解python屬性

在java等編譯語言中,我們傾向於將成員變數設為private,通過public的get/set方法來訪問對應的欄位,這樣做的好處在於get/set起到了攔截的作用,在這個點我們可以插入自己的邏輯,也就是常說的AOP(面向切面程式設計),比如日誌、快取、延遲建立等操作。 但是python並不

深入理解Python字串的用法

0. 拼接字串 字串的拼接操作最常用,我專門為這個話題寫過一篇《Python拼接字串的七種方式》,建議你回看。 在此,簡單回顧一下:七種拼接方式從實現原理上劃分為三類,即格式化類(%佔位符、format()、template)、拼接類(+操作符、類元祖方式、join())與插值類(f-string),在使

深入理解python字元編碼(包含2.x與3.x)

2018-11-29  09:44:30 引子    計算機要想工作必須通電,即用‘電’驅使計算機幹活,也就是說‘電’的特性決定了計算機的特性。 電的特性即高低電平(人類從邏輯上將二進位制數1對應高電平,二進位制數0對應低電平), 關於磁碟的磁特性也是同樣的道理。

深入學習Python解析並解密PDF檔案內容的方法

  前面學習瞭解析PDF文件,並寫入文件的知識,那篇文章的名字為深入學習Python解析並讀取PDF檔案內容的方法。   但是最近出現了一個新問題,就是上面使用pdfminer這個庫只能解析正常的PDF內容,然而在實際情況中,公司的一些文件可能是加密的,那麼如何處理加密的PDF檔案,就是本文學習的重點。