Python的import機制

來源:互聯網
上載者:User

標籤:install   機器   區別   pre   rac   /usr   href   不能   case   

模組與包

在瞭解 import 之前,有兩個概念必須提一下:

  • 模組: 一個 .py 檔案就是一個模組(module)
  • 包: __init__.py 檔案所在目錄就是包(package)

當然,這隻是極簡版的概念。實際上包是一種特殊的模組,而任何定義了 __path__ 屬性的模組都被當做包。只不過,咱們日常使用中並不需要知道這些。

兩種形式的 import

import 有兩種形式:

  • import ...
  • from ... import ...

兩者有著很細微的區別,先看幾行代碼。

from string import ascii_lowercaseimport stringimport string.ascii_lowercase

運行後發現最後一行代碼報錯:ImportError: No module named ascii_lowercase,意思是:“找不到叫 ascii_lowercase 的模組”。第 1 行和第 3 行的區別只在於有沒有 from,翻翻文法定義發現有這樣的規則:

  • import ... 後面只能是模組或包
  • from ... import ... 中,from 後面只能是模組或包,import 後面可以是任何變數

可以簡單的記成:第一個空只能填模組或包,第二個空填啥都行。

import 的搜尋路徑

提問,下面這幾行代碼的輸出結果是多少?

import stringprint(string.ascii_lowercase)

是小寫字母嗎?那可不一定,如果分類樹是這樣的:

./├── foo.py└── string.py

foo.py 所在目錄有叫 string.py 的檔案,結果就不確定了。因為你不知道 import string 到底是 import 了 ./string.py 還是標準庫的 string。為了回答這個問題,我們得瞭解一下 import 是怎麼找到模組的,這個過程比較簡單,只有兩個步驟:

  1. 搜尋「內建模組」(built-in module)
  2. 搜尋 sys.path 中的路徑

而 sys.path 在初始化時,又會按照順序添加以下路徑:

  1. foo.py 所在目錄(如果是軟連結,那麼是真正的 foo.py 所在目錄)或目前的目錄
  2. 環境變數 PYTHONPATH中列出的目錄(類似環境變數 PATH,由使用者定義,預設為空白);
  3. site 模組被 import 時添加的路徑1(site 會在運行時被自動 import)。

import site 所添加的路徑一般是 XXX/site-packages(Ubuntu 上是 XXX/dist-packages),比如在我的機器上是 /usr/local/lib/python2.7/site-packages。同時,通過 pip 安裝的包也是儲存在這個目錄下的。如果懶得記 sys.path的初始化過程,可以簡單的認為 import 的尋找順序是:

  1. 內建模組
  2. .py 檔案所在目錄
  3. pip 或 easy_install 安裝的包
相對 import 與 絕對 import相對 import

當項目規模變大,代碼複雜度上升的時候,我們通常會把一個一個的 .py 檔案組織成一個包,讓項目結構更加清晰。這時候 import 又會出現一些問題,比如:一個典型包的目錄結構是這樣的:

 string/├── __init__.py├── find.py└── foo.py

如果 string/foo.py 的代碼如下:

# string/foo.pyfrom string import findprint(find)

那麼 python string/foo.py 的運行結果會是下面的哪一個呢?

  • <module ‘string.find‘ from ‘string/find.py‘>
  • <function find at 0x123456789>

按我們前面講的各種規則來推導,因為 foo.py 所在目錄 string/ 沒有 string 模組(即 string.py),所以 import 的是標準庫的 string,答案是後者。不過,如果你把 foo 當成 string 包中的模組運行,即 python -m string.foo,會發現運行結果是前者。同樣的語句,卻有著兩種不同的語義,這無疑加重了咱們的心智負擔,總不能每次咱們調試包裡的模組時,都去檢查一下執行的命令是 python string/foo.py 還是 python -m string.foo 吧?

相對 import 就是專為解決「包內匯入」(intra-package import)而出現的。它的使用也很簡單,from 的後面跟個 . 就行:

# from string/ import find.pyfrom . import find# from string/find.py import *from .find import *

我們再看個複雜點的例子,有個包的目錄結構長這樣:

one/├── __init__.py├── foo.py└── two/    ├── __init__.py    ├── bar.py    └── three/        ├── __init__.py        ├── dull.py        └── run.py
from . import dullfrom .. import barfrom ... import fooprint(‘Go, go, go!‘)

改成

from .dull import *from ..bar import *from ...foo import *print(‘Go, go, go!‘)

結果是一樣的。

那麼 python string/foo.py 和 python -m string.foo 的運行結果又是怎樣呢?運行一下發現,兩者的輸出分別是:

Traceback (most recent call last):  File "string/foo.py", line 1, in <module>    from . import findValueError: Attempted relative import in non-package
<module ‘string.find‘ from ‘string/find.py‘>

原因在於 python string/foo.py 把 foo.py 當成一個單獨的指令碼來運行,認為 foo.py 不屬於任何包,所以此時相對 import 就會報錯。也就是說,無論命令列是怎麼樣的,運行時 import 的語義都統一了,不會再出現運行結果不一致的情況。

 

Python的import機制

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.