本系列文章是我學習Python3.9的官方tutorial的筆記,大部分來源于官網的中文翻譯平项,但由于該翻譯有些部分實在太差和啰嗦赫舒,我做了很多刪除和修改悍及,還有部分原文講不明白的,我參考其他資料增加了進一步闡述說明接癌。
模塊是一個包含Python定義和語句的文件心赶。文件名就是模塊名后跟文件后綴 .py 。在一個模塊內部缺猛,模塊名(作為一個字符串)可以通過全局變量 name 的值獲得缨叫。例如,使用你最喜愛的文本編輯器在當前目錄下創(chuàng)建一個名為 fibo.py 的文件荔燎, 文件中含有以下內容:
# Fibonacci numbers module
def fib(n): # write Fibonacci series up to n
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
print()
def fib2(n): # return Fibonacci series up to n
result = []
a, b = 0, 1
while a < n:
result.append(a)
a, b = b, a+b
return result
現在進入Python解釋器耻姥,并用以下命令導入該模塊:
>>> import fibo
你可以用模塊名訪問這些函數:
>>> fibo.fib(1000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
>>> fibo.fib2(100)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
>>> fibo.__name__
'fibo'
如果你想經常使用某個函數,你可以把它賦值給一個局部變量:
>>> fib = fibo.fib
>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
5.1 有關模塊的更多信息
模塊可以包含可執(zhí)行的語句以及函數定義有咨。這些語句用于初始化模塊琐簇。它們僅在模塊 第一次 在 import 語句中被導入時才執(zhí)行。
模塊可以導入其它模塊座享。習慣上但不要求把所有 import
語句放在模塊(或腳本)的開頭婉商。被導入的模塊名存放在調入模塊的全局符號表中。
import
語句有一個變體渣叛,它可以把名字從一個被調模塊內直接導入到現模塊的符號表里丈秩。例如:
>>> from fibo import fib, fib2
>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
還有一個變體甚至可以導入模塊內定義的所有名稱:
>>> from fibo import *
>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
這會調入所有非以下劃線(_)開頭的名稱。 在多數情況下诗箍,Python程序員都不會使用這個功能癣籽,因為它在解釋器中引入了一組未知的名稱,而它們很可能會覆蓋一些你已經定義過的東西滤祖。
如果模塊名稱之后帶有 as筷狼,則跟在 as 之后的名稱將直接綁定到所導入的模塊。
>>> import fibo as fib
>>> fib.fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
這種方式也可以在用到 from
的時候使用匠童,并會有類似的效果:
>>> from fibo import fib as fibonacci
>>> fibonacci(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
5.1.1. 以腳本的方式執(zhí)行模塊
當你用下面方式運行一個Python模塊:
python fibo.py <arguments>
模塊里的代碼會被執(zhí)行埂材,就好像你導入了模塊一樣,但是 name 被賦值為 "main"汤求。 這意味著通過在你的模塊末尾添加這些代碼:
if __name__ == "__main__":
import sys
fib(int(sys.argv[1]))
你既可以把這個文件當作腳本又可當作一個可調入的模塊來使用俏险, 因為那段解析命令行的代碼只有在當模塊是以“main”文件的方式執(zhí)行的時候才會運行:
$ python fibo.py 50
0 1 1 2 3 5 8 13 21 34
如果模塊是被導入的,那些代碼是不運行的扬绪。
5.1.2. 模塊搜索路徑
當一個名為 spam
的模塊被導入的時候竖独,解釋器首先尋找具有該名稱的內置模塊。如果沒有找到挤牛,解釋器會從 sys.path
變量給出的目錄列表里尋找名為 spam.py
的文件莹痢。sys.path
初始有這些目錄地址:
- 包含輸入腳本的目錄(或者未指定文件時的當前目錄)。
-
PYTHONPATH
(一個包含目錄名稱的列表,它和shell變量PATH
有一樣的語法)竞膳。 - 取決于安裝的默認設置
5.1.3 編譯過的python文件
為了加速模塊載入航瞭,Python在_pycache_ 目錄里緩存了每個模塊的編譯后版本,名稱為 module.version.pyc 坦辟,其中名稱中的版本字段對編譯文件的格式進行編碼刊侯; 它一般使用Python版本號。例如锉走,在CPython版本3.3中滨彻,spam.py的編譯版本將被緩存為 pycache/spam.cpython-33.pyc。此命名約定允許來自不同發(fā)行版和不同版本的Python的已編譯模塊共存挠日。
Python根據編譯版本檢查源的修改日期疮绷,以查看它是否已過期并需要重新編譯。這是一個完全自動化的過程嚣潜。此外冬骚,編譯的模塊與平臺無關,因此可以在具有不同體系結構的系統(tǒng)之間共享相同的庫懂算。
Python在兩種情況下不會檢查緩存只冻。首先,對于從命令行直接載入的模塊计技,它從來都是重新編譯并且不存儲編譯結果喜德;其次,如果沒有源模塊垮媒,它不會檢查緩存舍悯。為了支持無源文件(僅編譯)發(fā)行版本, 編譯模塊必須是在源目錄下睡雇,并且絕對不能有源模塊萌衬。
給專業(yè)人士的一些小建議:
- 你可以在Python命令中使用
-O
或者-OO
開關, 以減小編譯后模塊的大小它抱。-O
開關去除斷言語句秕豫,-OO
開關同時去除斷言語句和 doc 字符串观蓄。由于有些程序可能依賴于這些,你應當只在清楚自己在做什么時才使用這個選項侮穿。“優(yōu)化過的”模塊有一個opt-
標簽并且通常小些亲茅。將來的發(fā)行版本或許會更改優(yōu)化的效果金矛。 - 一個從
.pyc
文件讀出的程序并不會比它從.py
讀出時運行的更快,.pyc
文件唯一快的地方在于載入速度。 -
compileall
模塊可以為一個目錄下的所有模塊創(chuàng)建.pyc文件娶耍。 - 關于這個過程,PEP 3147 中有更多細節(jié)榕酒,包括一個決策流程圖。
5.2 標準模塊
Python附帶了一個標準模塊庫想鹰。一些模塊內置于解釋器中;它們提供對不屬于語言核心的一些內置操作的訪問辑舷,以提高效率和提供對系統(tǒng)調用的訪問喻犁。這些模塊的集合是一個配置選項肢础,它也取決于底層平臺。例如碌廓,winreg
模塊只在Windows操作系統(tǒng)上提供传轰。一個特別值得注意的模塊 sys
,它被內嵌到每一個Python解釋器中谷婆。變量 sys.ps1
和 sys.ps2
定義用作主要和輔助提示的字符串:
>>> import sys
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>> sys.ps1 = 'C> '
C> print('Yuck!')
Yuck!
C>
這兩個變量只有在編譯器是交互模式下才被定義慨蛙。
sys.path
變量是一個字符串列表撞反,用于確定解釋器的模塊搜索路徑锚烦。該變量被初始化為從環(huán)境變量 PYTHONPATH
獲取的默認路徑,或者如果 PYTHONPATH
未設置明郭,則從內置默認路徑初始化廷区。你可以使用標準列表操作對其進行修改:
>>> import sys
>>> sys.path.append('/ufs/guido/lib/python')
5.3 dir()函數
內置函數 dir()
可以列出某個對象定義的所有屬性唯灵。 它返回一個排序過的字符串列表:
>>> import fibo, sys
>>> dir(fibo)
['__name__', 'fib', 'fib2']
>>> dir(sys)
['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__',
'__interactivehook__', '__loader__', '__name__', '__package__', '__spec__',
'__stderr__', '__stdin__', '__stdout__', '__unraisablehook__',
'_clear_type_cache', '_current_frames', '_debugmallocstats', '_framework',
'_getframe', '_git', '_home', '_xoptions', 'abiflags', 'addaudithook',
'api_version', 'argv', 'audit', 'base_exec_prefix', 'base_prefix',
'breakpointhook', 'builtin_module_names', 'byteorder', 'call_tracing',
'callstats', 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_info',
'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info',
'float_repr_style', 'get_asyncgen_hooks', 'get_coroutine_origin_tracking_depth',
'getallocatedblocks', 'getdefaultencoding', 'getdlopenflags',
'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile',
'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval',
'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info',
'intern', 'is_finalizing', 'last_traceback', 'last_type', 'last_value',
'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks',
'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'pycache_prefix',
'set_asyncgen_hooks', 'set_coroutine_origin_tracking_depth', 'setdlopenflags',
'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr',
'stdin', 'stdout', 'thread_info', 'unraisablehook', 'version', 'version_info',
'warnoptions']
如果沒有參數,dir()
會列出你當前定義的名稱:
>>> a = [1, 2, 3, 4, 5]
>>> import fibo
>>> fib = fibo.fib
>>> dir()
['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']
注意:它會列出所有類型的名稱:包括變量隙轻,模塊埠帕,函數,等等玖绿。
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', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented',
'NotImplementedError', 'OSError', 'OverflowError',
'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError',
'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning',
'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError',
'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError',
'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError',
'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning',
'ValueError', 'Warning', 'ZeroDivisionError', '_', '__build_class__',
'__debug__', '__doc__', '__import__', '__name__', '__package__', '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']
5.4 包
包是一種通過用“帶點號的模塊名”來構造 Python 模塊命名空間的方法斑匪。 例如呐籽,模塊名 A.B 表示 A 包中名為 B 的子模塊。
這是你的包的可能結構(以分層文件系統(tǒng)的形式表示):
sound/ Top-level package
__init__.py Initialize the sound package
formats/ Subpackage for file format conversions
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/ Subpackage for sound effects
__init__.py
echo.py
surround.py
reverse.py
...
filters/ Subpackage for filters
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
當導入這個包時,Python搜索 sys.path 里的目錄狡蝶,查找包的子目錄庶橱。
必須要有 init.py 文件才能讓 Python 將包含該文件的目錄當作包。 在最簡單的情況下贪惹,init.py 可以只是一個空文件苏章,但它也可以執(zhí)行包的初始化代碼或設置 all 變量,具體將在后文介紹奏瞬。
從包中導入單個模塊:
import sound.effects.echo
這會加載子模塊 sound.effects.echo 枫绅。但引用它時必須使用它的全名。
sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
導入子模塊的另一種方法是
from sound.effects import echo
這也會加載子模塊 echo 硼端,并使其在沒有包前綴的情況下可用并淋,因此可以按如下方式使用:
echo.echofilter(input, output, delay=0.7, atten=4)
請注意,當使用 from package import item
時珍昨,item可以是包的子模塊(或子包)县耽,也可以是包中定義的其他名稱曼尊,如函數,類或變量瞒御。 import
語句首先測試是否在包中定義了item神郊;如果沒有,它假定它是一個模塊并嘗試加載它蜻懦。如果找不到它夕晓,則引發(fā) ImportError
異常。
相反征炼,當使用 import item.subitem.subsubitem
這樣的語法時谆奥,除了最后一項之外的每一項都必須是一個包拂玻;最后一項可以是模塊或包宰译,但不能是類或函數或變量沿侈。
5.4.1 子包相互引用
當包被構造成子包時(與示例中的 sound 包一樣)肋坚,你可以使用絕對導入來引用兄弟包的子模塊肃廓。例如盲赊,如果模塊 sound.filters.vocoder 需要使用 sound.effects 包中的 echo 模塊哀蘑,它可以使用 from sound.effects import echo 葵第。
你還可以相對路徑的形式來導入:
from . import echo
from .. import formats
from ..filters import equalizer
from ..filters.
請注意卒密,相對導入是基于當前模塊的名稱進行導入的。由于主模塊的名稱總是 "main" 膛腐,因此用作Python應用程序主模塊的模塊必須始終使用絕對導入鼎俘。