包和模塊、import
簡單的說,Python 中一個 以 .py 結尾的 Python 文件,就是一個模塊(Module),包含了 Python 對象定義和 Python 語句。
包就是文件夾,但該文件夾下必須存在 __init__.py
文件, 該文件的內容可以為空。__init__.py
用於標識當前文件夾是一個包。
模塊
Python 中一個 以 .py 結尾的 Python 文件,就是一個模塊(Module),包含了 Python 對象定義和 Python 語句。模塊能夠讓你有邏輯地組織 Python 代碼段。把相關的代碼分配到一個模塊里能讓代碼更好用,更易懂。模塊里既能定義函數,類和變量,也能包含可執行的代碼。
模塊的引入
當解釋器遇到 import 語句,如果模塊在當前的搜索路徑就會被導入。搜索路徑是一個解釋器會先進行搜索的所有目錄的列表。不過,不管你執行了多少次 import,一個模塊只會被導入一次(第一次導入后就將模塊名加載到內存了,后續的 import 語句僅是對已經加載大內存中的模塊對象增加了一次引用,不會重新執行模塊內的語句)。這樣可以防止導入模塊被一遍又一遍地執行。如果你想重新執行模塊里頂層部分的代碼,可以用 reload(modul_name) 函數。該函數會重新導入之前導入過的模塊。Python 3 移動到了模塊 importlib 中。
import ...
語句
from ... import ...
語句:Python 的 from 語句讓你從模塊中導入一個指定的部分到當前命名空間中。小心與自定義的對象重名,導致被覆蓋掉了。
from ... import *
語句:導入模塊中定義的全部公有名稱。這種聲明不要過多地使用,可讀性極差,而且導入了過多不必要的對象。不過,有時會與 __all__
一起使用,此時,會一次性導入 __all__
變量中列出的所有模塊。
PEP8 建議:
1.py 文件中導入模塊的順序:1)Python內置模塊;2)第三方模塊;3)自定義模塊;且應在每個導入組之間放一行空行。
2.模塊級別的內置屬性(名字有前后雙下划線的),例如 __all__
, __author__
, __version__
,應該放置在模塊的文檔字符串后,任意 import 語句之前,from __future__
導入除外。Python強制要求from __future__
導入必須在任何代碼之前,只能在模塊級文檔字符串之后。
from __future__ import barry_as_FLUFL
__all__ = ['a', 'b', 'c']
__version__ = '0.1 '
__author__ = 'Cardinal Biggles'
import os
import sys
搜索路徑
當你導入一個模塊,Python 解析器對模塊位置的搜索順序是:
1、當前目錄;
2、如果不在當前目錄,Python 則搜索在 shell 變量 PYTHONPATH 下的每個目錄;
3、如果都找不到,Python 會查看默認路徑。UNIX下,默認路徑一般為/usr/local/lib/python/。
模塊搜索路徑存儲在 system 模塊的 sys.path (list,指明了所有查找 module,package 的路徑)變量中。變量里包含當前目錄,PYTHONPATH和由安裝過程決定的默認目錄。
包
包是一個分層次的文件目錄結構,它定義了一個由模塊及子包,和子包下的子包等組成的 Python 的應用環境。
簡單的說,包就是文件夾,但該文件夾下必須存在 __init__.py
文件, 該文件的內容可以為空,也可以有 Python 代碼,原則是盡量保持 __init__.py
文件的精簡。__init__.py
用於標識當前文件夾是一個包。若這個文件不存在,那么這個目錄只是一個目錄而不是一個包。有如下目錄的 m3.py、test1.py
├─root
│ ├─pack1
│ │ ├─pack2
│ │ │ ├─pack3
│ │ │ │ ├─__init__.py
│ │ │ │ ├─m3.py
│ │ ├─__init__.py
│ │ ├─m2.py
│ ├─test1.py
# m3.py
def pack3_print():
print("我是m3!")
pack_3 中的 __init__.py
文件中為空,m3.py 有一個打印函數 pack3_print()
。由於調用包的其他模塊所在的絕對路徑是千變萬化的,所以在包的內部調用自身其他文件中的函數、常量、類,應該使用相對路徑,而不是絕對路徑。
1.現在若要在 test1.py
文件夾調用這個函數,可以這么做:
1)按照路徑導入即可:
from root.pack1.pack2.pack3.m3 import pack3_print
pack3_print()
or
import pack1.pack2.pack3.m3
pack1.pack2.pack3.m3.pack3_print()
2)在 test1.py 同級目錄的 pack1 的文件夾下創建一個 __init__.py
文件, 文件內容是:
from .pack2.pack3.m3 import pack3_print
再在 test1.py 中引入 pack3_print 函數:
from pack1 import pack3_print
pack3_print()
from . import <some-module>
句法表示相對導入。語句中的 . 表示當前包。還有 from .. import <some-module>
,這里的 .. 表示當前包的上一層。
包內部的文件之間互相導入可以使用相對導入,並且通過提前把函數、常量、類導入到 __init__.py
中后,包外面再想導入這些內容時,就可以直接用 from 包名 import 函數名
導入了,可以精簡代碼。
2.我們再在 pack2 下創建一個 m2.py 文件,在 m2 中調用 pack3_print 函數:
# 在 m2.py 使用相對路徑直接導入:
from .pack2.pack3.m3 import pack3_print
def pack2_print():
print('我是m2!')
pack3_print()
不過,由於前面我們已經在 __init__.py
導入過 pack3_print 函數了,所以,我們可以在 m2 中這樣直接導入:
from . import pack3_print -- 注意一下循環導入依賴的問題?
我們再修改上面的 __init__.py
,把 pack2_print
函數導入進去:
from .pack2.pack3.m3 import pack3_print
from .m2 import pack2_print
最后,在 test1.py
中調用這個 pack2_print
函數:
from pack1 import pack3_print
from pack1 import pack2_print
pack3_print()
pack2_print()