上一節,我們講解了Python模組的基礎知識,這一節我們繼續深入瞭解模組的更多知識,從而讓大家全面瞭解、掌握和運用模組到我們實際的程式設計中。
在上一節中有一句話“接著我們在這個檔案所在目錄執行Python直譯器IPython”,不知道大家還記不記得。這就話背後隱含的意思是,在這個檔案(模組)目錄下,我們才可以import到這個模組,否則會報錯,說找不到這個模組。這裡,就涉及到了Python模組的搜尋路徑。
Python模組的搜尋路徑
當一個名為mylib
的模組被匯入時,直譯器首先搜尋內建模組是不是有該名字的模組。
如果沒有找到,接著搜尋sys.path
列出的目錄下面是不是有名為mylib
的模組。
sys.path
的初始化按以下幾個路徑的順序:
(1)包含輸入指令碼的目錄,如果沒有輸入指令碼則是當前目錄;
(2)環境變數PYTHONPATH(一個目錄名稱的列表);
(3)Python庫的安裝目錄。
下面我們來驗證一下sys.path
所包含的路徑,編寫一個Python檔案initpath.py
如下:
# Author: veelion
# file: initpath.py
import sys
print('\n'.join(sys.path))
命令列下執行:python3 initpath.py
得到如下結果:
$ python mylib.py
/home/veelion/p2/tutorial/md_Python/codes
/home/veelion/.virtualenvs/py3.7/lib/python37.zip
/home/veelion/.virtualenvs/py3.7/lib/python3.7
/home/veelion/.virtualenvs/py3.7/lib/python3.7/lib-dynload
/usr/lib/python3.7
/home/veelion/.virtualenvs/py3.7/lib/python3.7/site-packages
我們可以發現,initpath.py
所在的目錄是sys.path
列表的第一個元素。符合上面三條原則的順序。
接下來我們透過互動式Python直譯器來看看sys.path
,執行CPython直譯器再匯入sys:
>>> import sys
>>> sys.path
['', '/home/veelion/.virtualenvs/py3.7/lib/python37.zip', '/home/veelion/.virtualenvs/py3.7/lib/python3.7', '/home/veelion/.virtualenvs/py3.7/lib/python3.7/lib-dynload', '/usr/lib/python3.7', '/home/veelion/.virtualenvs/py3.7/lib/python3.7/site-packages']
細心的小猿可以發現,sys.path
的第一項是個空字串,和執行指令碼方式下的第一項——當前路徑不一樣。為什麼會是空字串呢?
這是因為,當互動式執行Python直譯器時(或者指令碼是從標註輸入讀取的),可以認為傳給直譯器的指令碼檔案路徑為空,那麼就把sys.path[0]
設定為空字串,它告訴Python搜尋模組時先從當前資料夾開始。
以上兩種方法驗證的sys.path
都符合預期,然而IPython有點例外。
In [1]: import sys
In [2]: sys.path
Out[2]:
['/home/veelion/.virtualenvs/py3.7/bin',
'/home/veelion/.virtualenvs/py3.7/lib/python37.zip',
'/home/veelion/.virtualenvs/py3.7/lib/python3.7',
'/home/veelion/.virtualenvs/py3.7/lib/python3.7/lib-dynload',
'/usr/lib/python3.7',
'',
'/home/veelion/.virtualenvs/py3.7/lib/python3.7/site-packages',
'/home/veelion/.virtualenvs/py3.7/lib/python3.7/site-packages/IPython/extensions',
'/home/veelion/.ipython']
IPython 並沒有把當前路徑放在第一項。這樣就會導致你寫的模組與系統模組重名時,它import的是系統模組而不是你寫的模組,而前面兩種方式就是匯入你寫的模組而非系統模組。這一點在使用IPython時要格外注意。
注意你可以在程式中修改sys.path
。sys.path
是一個Python的列表結構,我們可以像修改列表那樣修改它,增加、刪除、修改路徑順序。比如,可以透過sys.path.insert(0, 'my-module-path')
來把我們自己寫的模組的路徑放到搜尋路徑的最前面,優先搜尋自己的模組。
編譯後的Python檔案:*pyc
為了加速模組的載入時間,Python會快取已經編譯好的模組,並把它們放在與模組同級目錄下的__pycache__
資料夾下面,編譯好的模組的命名方式為:module.version.pyc
,其中的version
包含Python的版本號。比如:
$ ls __pycache__/
m1.cpython-36.pyc m2.cpython-36.pyc
cpython-36
就是編譯這個模組的Python資訊:用CPython 3.6 進行編譯的。這種命名方式方便不同版本的Python編譯的模組同時存在而不造成衝突。
Python在兩種情況下不檢查快取。
其一,它總是重新編譯並且不儲存直接從命令列載入的模組的結果。
其二,如果沒有模組原始碼檔案,它不會檢查快取。要支援非源(僅編譯)分發,已編譯的模組必須位於原始碼目錄中,並且不得有模組原始碼。
舉個例子理解一下這兩點:
(1)如果在命令列下執行python m1.py
,Python總是從新編譯m1.py
,但不會儲存pyc檔案,因為每次都有重新編譯就沒必要儲存了。
(2)如果我們匯入m1
模組時,搜尋路徑目錄下只有m1.pyc
而沒有m1.py
檔案,那就直接匯入m1.pyc
。這種方式適合把編譯好的pyc釋出給其他人而不是給它們原始碼,使用這種方式時,把.pyc
檔案從__pycache__
中複製到.py
檔案相同的目錄下並刪掉.py
檔案即可。
Python模組的高階技巧
(1)模組compileall
可以把一個資料夾下所有的py檔案編譯成.pyc檔案。
它的使用很簡單,命令列執行的格式如下:
python -m compileall 資料夾或檔名
更多選項可以透過:python -m compileall -h
檢視。
(2)編譯成.pyc
檔案時,可以給Python命令兩個選項:-O
和-OO
,使得編譯後的檔案更小。
-O 去除assert語句;
-OO 去除assert語句和__doc__ string
。
根據情況來使用這兩個選項,用compileall編譯檔案時加這個選項就是這樣子的:
python -O -m compileall 資料夾或檔名
生成的pyc檔名稱裡面有opt-
標籤,-O
的標籤是opt-
,-OO
的標籤就是opt-2
。比如:
$ ls -F -1 __pycache__/
m1.cpython-36.opt-1.pyc
m1.cpython-36.opt-2.pyc
m1.cpython-36.pyc
(3).pyc
和.py
檔案都不會使程式執行得更快(不會提高執行速度)。但是,.pyc
檔案能使載入速度更快,因為少了編譯的過程。
Python標準模組
Python附帶了一個標準模組庫。其中一些模組內建在直譯器中,它們提供對不屬於語言核心但仍然內建的操作的訪問,以提高效率或提供對系統呼叫等作業系統原語的訪問。這些模組的集合是一個配置選項,它也取決於底層平臺。例如,winreg
模組僅在Windows系統上提供。一個值得注意的模組是sys
,它內建於每個Python直譯器中。
Python的標註模組會在我們今後的程式設計中不斷遇到和使用,具體的學習可以在今後用到時再學習。你需要記住的一點是:當你寫Python程式碼需要某些基本功能時,一定要先找找看是否已經有標準模組存在,是否已經有人寫好了包含這些功能的模組,最後才要覺得自己要不要實現這些功能。
Python內建函式dir()
dir()用來檢視模組裡面定義的名字,包括:變數名,模組名,函式名等等。
它返回一個list:
In [5]: import my_first_module
In [6]: dir(my_first_module)
Out[6]:
['MY_NAME',
'__builtins__',
'__cached__',
'__doc__',
'__file__',
'__loader__',
'__name__',
'__package__',
'__spec__',
'my_add',
'my_print']
如果呼叫dir()不傳遞引數,則列出當前已經定義的所有名字:
In [1]: import my_first_module
In [2]: dir()
Out[2]:
['In',
'Out',
'_',
'__',
'___',
'__builtin__',
'__builtins__',
'__doc__',
'__loader__',
'__name__',
'__package__',
'__spec__',
'_dh',
'_i',
'_i1',
'_i2',
'_ih',
'_ii',
'_iii',
'_oh',
'exit',
'get_ipython',
'my_first_module',
'quit']
用dir()可以檢視所有的內建型別、變數、函式等,方法是藉助標準模組builtins
:
>>> import builtins
>>> dir(builtins)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
Python模組高階技巧總結
(1)Python的搜尋路徑,順序一定要搞得清;
(2)編譯後的.pyc
檔案;
(3)dir()內建函式檢視模組定義的名字。
我的公眾號:猿人學 Python 上會分享更多心得體會,敬請關注。
***版權申明:若沒有特殊說明,文章皆是猿人學 yuanrenxue.com 原創,沒有猿人學授權,請勿以任何形式轉載。***