import <module_name>
對于語句 import <module_name>
,實際將module_name.py文件的代碼作為一個<class 'module'>對象加載到內存,放在當前文件上下文中。
這是,在當前上下文中,可以用點操作符調用module中的成員(import的目標對象也是成員)。比如:
// lib.py
a = 1
// main.py
import lib
print(lib.a)
需要注意的是,這個module文件必須在一些目錄下(一層),才能夠被這樣import。
這些目錄保存于數組sys.path
。
也就是說,解釋器對于import <module_name>
,會去sys.path
包含的目錄下,逐個去找名為module_name.py的文件。
一般情況下,sys.path
數組中首個路徑是所執行文件所在路徑,即入口文件中的os.path.dirname(__file__)
。其余是python自身提供的一些路徑,包括第三方模塊所在路徑。
所以,導入時,首個路徑下的同名文件,可以覆蓋第三方模塊。
from <p/a/t/h> import <module_name/item_name>
對于語句 from x import y
,第一個參數x,是引入路徑,可以有多層。最后一層可以是module_name。
第二個參數y是引入對象,可以是module_name,也可以是item_name,即模塊中的成員。
from x import y
相對于import y
,增加了從多級路徑中導入,和只導入部分成員的能力。
比如:
// lib/lib.py
a = 1
// main.py
from lib imort lib
from lib.lib import a
print(lib.a)
print(a)
上面展示的引入路徑,是絕對路徑(相對于sys.path
的)。path的首層,必須是sys.path
目錄的成員(文件或目錄)。
還有一種相對引入路徑,使用.
開頭,用于子目錄中相互引用。比如:
// lib/lib1.py
from . import lib2
from .lib2 import a
// lib/lib2.py
a = 1
// main.py
from lib import lib1
from lib.lib1 import lib2, a
這里.
就表示導入文件中的os.path.dirname(__file__)
。其余規則同上。
修改sys.path
sys.path
是可寫的。這使得我們可以用它來做一些特別處理。
一個案例是:
python使用grpc開發時,需要將x.proto原型定義文件,編譯為x_pb2.py和x_pb2_grpc.py文件。
使用命令行工具進行編譯,編譯結果會輸出到一個指定的目錄。如果這個目錄和引入x_pb2.py和x_pb2_grpc.py的文件所在是同個目錄,那就沒有問題。
如果希望將之存放到一個子目錄,就會產生異常了:
- main.py
- proto/
- - x_pb2.py
- - x_pb2_grpc.py
因為x_pb2_grpc.py會引入x_pb2.py,語句為import x_pb2 as x__pb2
。而proto/不在sys.path
中,這個引用是錯誤的。
因此,可以通過增加目錄到sys.path
來實現修正:
// main.py
import os
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), 'proto'))
from proto import x_pb2_grpc
from proto import x_pb2