6. 模組 (Module)
****************

如果從 Python 直譯器離開後又再次進入，之前（幫函式或變數）做的定義都會
消失。因此，想要寫一些比較長的程式時，你最好使用編輯器來準備要輸入給直
譯器的內容，並且用該檔案來運行它。這就是一個*腳本 (script)*。隨著你的
程式越變越長，你可能會想要把它分開成幾個檔案，讓它比較好維護。你可能也
會想用一個你之前已經在其他程式寫好的函式，但不想要複製該函式的原始定義
到所有使用它的程式裡。

為了支援這一點，Python 有一種方法可以將定義放入檔案中，並在互動模式下
的直譯器中使用它們。這種檔案稱為*模組 (module)*；模組中的定義可以被
*import* 到其他模組中，或是被 *import* 至*主 (main)* 模組（在最頂層執
行的腳本，以及互動模式下，所使用的變數集合）。

模組是指包含 Python 定義和語句的檔案，檔案名稱是模組名稱加上 ".py"。在
模組中，模組的名稱（作為字串）會是全域變數 "__name__" 的值。例如，用您
喜歡的文字編輯器在資料夾中創一個名為 "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 這個模組：

   >>> import fibo

這並不會將 "fibo" 中定義的函式名稱直接加入當前的 *namespace* 中（詳情
請見 Python 作用域 (Scope) 及命名空間 (Namespace)）；它只會加入 "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


6.1. 深入了解模組
=================

模組可以包含可執行的陳述式以及函式的定義。這些陳述式是作為模組的初始化
，它們只會在*第一次*被 import 時才會執行。[1]（如果檔案被當成腳本執行
，也會執行它們）。

每個模組都有它自己的私有命名空間 (namespace)，模組內定義的函式會把該模
組的私有符號表當成全域命名空間使用。因此，模組的作者可以在模組中使用全
域變數，而不必擔心和使用者的全域變數發生意外的名稱衝突。另一方面，如果
你知道自己在做什麼，你可以用這個方式取用模組的全域變數，以和引用函式一
樣的寫法，"modname.itemname"。

在一個模組中可以 import 其他模組。把所有的 "import" 陳述式放在模組（就
這邊來說，腳本也是一樣）的最開頭是個慣例，但並沒有強制。如放置在模組的
最高層（不在任何函式或 class 中），被 import 的模組名稱將被加入全域命
名空間中。

"import" 陳述式有另一種變形寫法，可以直接將名稱從欲 import 的模組，直
接 import 至原模組的命名空間中。例如：

   >>> from fibo import fib, fib2
   >>> fib(500)
   0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

在 import 之後的名稱會被導入，但定義該函式的整個模組名稱並不會被引入在
區域命名空間中（因此，示例中的 "fibo" 未被定義）。

甚至還有另一種變形寫法，可以 import 模組定義的所有名稱：

   >>> from fibo import *
   >>> fib(500)
   0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

這個寫法會 import 模組中所有的名稱，除了使用底線（"_"）開頭的名稱。大
多數情況下，Python 程式設計師不大使用這個功能，因為它在直譯器中引入了
一組未知的名稱，並且可能覆蓋了某些您已經定義的內容。

請注意，一般情況下並不建議從模組或套件中 import "*" 的做法，因為它通常
會導致可讀性較差的程式碼。但若是使用它來在互動模式中節省打字時間，則是
可以接受的。

如果模組名稱後面出現 "as"，則 "as" 之後的名稱將直接和被 import 模組綁
定在一起。

   >>> import fibo as fib
   >>> fib.fib(500)
   0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

這個 import 方式和 "import fibo" 實質上是一樣的，唯一的差別是現在要用
"fib" 使用模組。

在使用 "from" 時也可以用同樣的方式獲得類似的效果：

   >>> from fibo import fib as fibonacci
   >>> fibonacci(500)
   0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

備註:

  出於效率原因，每個模組在每個直譯器 session 中僅會被 import 一次。因
  此，如果您更改了模組，則必須重啟直譯器——或者，如果只是一個想要在互動
  模式下測試的模組，可以使用 "importlib.reload()"。例如："import
  importlib; importlib.reload(modulename)"。


6.1.1. 把模組當作腳本執行
-------------------------

當使用以下內容運行 Python 模組時：

   python fibo.py <arguments>

如同使用 import 指令，模組中的程式碼會被執行，但 "__name__" 被設為
""__main__""。這意味著，透過在模組的末尾添加以下程式碼：

   if __name__ == "__main__":
       import sys
       fib(int(sys.argv[1]))

你可以將檔案作為腳本也同時可以作為被 import 的模組，因為剖析 (parse)
命令列的程式碼只會在當模組是「主」檔案時，才會執行：

   $ python fibo.py 50
   0 1 1 2 3 5 8 13 21 34

如果此模組是被 import 的，則該段程式碼不會被執行：

   >>> import fibo
   >>>

這通常是用來為模組提供方便的使用者介面，或者用於測試目的（執行測試套件
時，以腳本的方式執行模組）。


6.1.2. 模組的搜尋路徑
---------------------

When a module named "spam" is imported, the interpreter first searches
for a built-in module with that name. These module names are listed in
"sys.builtin_module_names". If not found, it then searches for a file
named "spam.py" in a list of directories given by the variable
"sys.path".  "sys.path" is initialized from these locations:

* 輸入腳本所位在的資料夾（如未指定檔案時，則是當前資料夾）。

* "PYTHONPATH"（一連串和 shell 變數 "PATH" 的語法相同的資料夾名稱）。

* 與安裝相關的預設值（按慣例會包含一個 "site-packages" 資料夾，它是由
  "site" 模組所處理）。

在 The initialization of the sys.path module search path 有更多的細節
。

備註:

  在支援符號連結 (symlink) 的檔案系統中，輸入腳本的所在資料夾是在跟隨
  符號連結之後才被計算的。換言之，包含符號連結的資料夾**並沒有**增加到
  模組的搜尋路徑中。

初始化之後，Python 程式可以修改 "sys.path"。執行中腳本的所在資料夾會在
搜尋路徑的開頭，在標準函式庫路徑之前。這代表該資料夾中的腳本會優先被載
入，而不是函式庫資料夾中相同名稱的模組。除非是有意要做這樣的替換，否則
這是一個錯誤。 請參見標準模組以瞭解更多資訊。


6.1.3. 「編譯」Python 檔案
--------------------------

為了加快載入模組的速度，Python 將每個模組的編譯版本暫存在
"__pycache__" 資料夾下，並命名為 "module.*version*.pyc"， 這裡的
version 是編譯後的檔案的格式名稱，且名稱通常會包含 Python 的版本編號。
例如，在 CPython 3.3 中，spam.py 的編譯版本將被暫存為
"__pycache__/spam.cpython-33.pyc"。此命名準則可以讓來自不同版本的編譯
模組和 Python 的不同版本同時共存。

Python 根據原始碼最後修改的日期，檢查編譯版本是否過期而需要重新編譯。
這是一個完全自動的過程。另外，編譯後的模組獨立於平台，因此不同架構的作
業系統之間可以共用同一函式庫。

Python 在兩種情況下不檢查快取 (cache)。首先，它總是重新編譯且不儲存直
接從命令列載入的模組的結果。第二，如果沒有源模組，則不會檢查快取。要支
援非源模組（僅編譯）的發布，編譯後的模組必須位於原始資料夾中，並且不能
有源模組。

一些給專家的秘訣：

* 可以在 Python 指令上使用開關參數 (switch) "-O" 或 "-OO" 來減小已編譯
  模組的大小。開關參數 "-O" 刪除 assert（斷言）陳述式，而 "-OO" 同時刪
  除 assert 陳述式和 __doc__ 字串。由於有些程式可能依賴於上述這些內容
  ，因此只有在您知道自己在做什麼時，才應使用此參數。「已優化」模組有
  "opt-" 標記，且通常較小。未來的版本可能會改變優化的效果。

* 讀取 ".pyc" 檔案時，程式的執行速度並不會比讀取 ".py" 檔案快。唯一比
  較快的地方是載入的速度。

* 模組 "compileall" 可以為資料夾中的所有模組創建 .pyc 檔。

* 更多的細節，包括決策流程圖，請參考**PEP 3147**。


6.2. 標準模組
=============

Python 附帶了一個標準模組庫，詳細的介紹在另一份文件，稱為「Python 函式
庫參考手冊」（簡稱為「函式庫參考手冊」）。有些模組是直譯器中內建的；它
們使一些不屬於語言核心但依然內建的運算得以存取，其目的是為了提高效率，
或提供作業系統基本操作（例如系統呼叫）。這些模組的集合是一個組態選項，
它們取決於底層平台。例如："winreg" 模組僅供 Windows 使用。值得注意的模
組是 "sys"，它被內建在每個 Python 直譯器中。變數 "sys.ps1" 和
"sys.ps2" 則用來定義主、次提示字元的字串：

   >>> import sys
   >>> sys.ps1
   '>>> '
   >>> sys.ps2
   '... '
   >>> sys.ps1 = 'C> '
   C> print('Yuck!')
   Yuck!
   C>

只有直譯器在互動模式時，才需要定義這兩個變數。

變數 "sys.path" 是一個字串 list，它決定直譯器的模組搜尋路徑。它的初始
值為環境變數 "PYTHONPATH" 中提取的預設路徑，或是當 "PYTHONPATH" 未設定
時，從內建預設值提取。你可以用標準的 list 操作修改該變數：

   >>> import sys
   >>> sys.path.append('/ufs/guido/lib/python')


6.3. "dir()" 函式
=================

內建函式 "dir()" 用於找出模組定義的所有名稱。它回傳一個排序後的字串
list：

   >>> 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']


6.4. 套件 (Package)
===================

Packages are a way of structuring Python's module namespace by using
"dotted module names".  For example, the module name "A.B" designates
a submodule named "B" in a package named "A".  Just like the use of
modules saves the authors of different modules from having to worry
about each other's global variable names, the use of dotted module
names saves the authors of multi-module packages like NumPy or Pillow
from having to worry about each other's module names.

假設你想設計一個能統一處理音訊檔案及音訊數據的模組集（「套件」）。因為
音訊檔案有很多的不同的格式（通常以它們的副檔名來辨識，例如：".wav"，
".aiff"，".au"），因此，為了不同檔案格式之間的轉換，你會需要建立和維護
一個不斷增長的模組集合。為了要達成對音訊數據的許多不同作業（例如，音訊
混合、增加回聲、套用等化器功能、創造人工立體音效），你將編寫一系列無止
盡的模組來執行這些作業。以下是你的套件可能的架構（以階層式檔案系統的方
式表示）：

   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
                 ...

Import 套件時，Python 會搜尋 "sys.path" 裡的目錄，尋找套件的子目錄。

The "__init__.py" files are required to make Python treat directories
containing the file as packages (unless using a *namespace package*, a
relatively advanced feature). This prevents directories with a common
name, such as "string", from unintentionally hiding valid modules that
occur later on the module search path. In the simplest case,
"__init__.py" can just be an empty file, but it can also execute
initialization code for the package or set the "__all__" variable,
described later.

套件使用者可以從套件中 import 個別模組，例如：

   import sound.effects.echo

This loads the submodule "sound.effects.echo".  It must be referenced
with its full name.

   sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)

另一種 import 子模組的方法是：

   from sound.effects import echo

This also loads the submodule "echo", and makes it available without
its package prefix, so it can be used as follows:

   echo.echofilter(input, output, delay=0.7, atten=4)

另一種變化是直接 import 所需的函式或變數：

   from sound.effects.echo import echofilter

Again, this loads the submodule "echo", but this makes its function
"echofilter()" directly available:

   echofilter(input, output, delay=0.7, atten=4)

請注意，使用 "from package import item" 時，item 可以是套件的子模組（
或子套件），也可以是套件中被定義的名稱，像是函式、class （類別）或變數
。"import" 陳述式首先測試套件中有沒有定義該 item；如果沒有，則會假設它
是模組，並嘗試載入。如果還是找不到 item，則會引發 "ImportError" 例外。

相反地，使用 "import item.subitem.subsubitem" 語法時，除了最後一項之外
，每一項都必須是套件；最後一項可以是模組或套件，但不能是前一項中定義的
class、函式或變數。


6.4.1. 從套件中 import *
------------------------

當使用者寫下 "from sound.effects import *" 時，會發生什麼事？理想情況
下，我們可能希望程式碼會去檔案系統，尋找套件中存在的子模組，並將它們全
部 import。這會花費較長的時間，且 import 子模組的過程可能會有不必要的
副作用，這些副作用只有在明確地 import 子模組時才會發生。

唯一的解法是由套件作者為套件提供明確的索引。"import" 陳述式使用以下慣
例：如果套件的 "__init__.py" 程式碼有定義一個名為 "__all__" 的 list，
若遇到 "from package import *" 的時候，它就會是要被 import 的模組名稱
。發布套件的新版本時，套件作者可自行決定是否更新此 list。如果套件作者
認為沒有人會從他的套件中 import *，他也可能會決定不支援這個 list。舉例
來說，"sound/effects/__init__.py" 檔案可包含以下程式碼：

   __all__ = ["echo", "surround", "reverse"]

This would mean that "from sound.effects import *" would import the
three named submodules of the "sound.effects" package.

Be aware that submodules might become shadowed by locally defined
names. For example, if you added a "reverse" function to the
"sound/effects/__init__.py" file, the "from sound.effects import *"
would only import the two submodules "echo" and "surround", but *not*
the "reverse" submodule, because it is shadowed by the locally defined
"reverse" function:

   __all__ = [
       "echo",      # refers to the 'echo.py' file
       "surround",  # refers to the 'surround.py' file
       "reverse",   # !!! refers to the 'reverse' function now !!!
   ]

   def reverse(msg: str):  # <-- this name shadows the 'reverse.py' submodule
       return msg[::-1]    #     in the case of a 'from sound.effects import *'

If "__all__" is not defined, the statement "from sound.effects import
*" does *not* import all submodules from the package "sound.effects"
into the current namespace; it only ensures that the package
"sound.effects" has been imported (possibly running any initialization
code in "__init__.py") and then imports whatever names are defined in
the package.  This includes any names defined (and submodules
explicitly loaded) by "__init__.py".  It also includes any submodules
of the package that were explicitly loaded by previous "import"
statements.  Consider this code:

   import sound.effects.echo
   import sound.effects.surround
   from sound.effects import *

In this example, the "echo" and "surround" modules are imported in the
current namespace because they are defined in the "sound.effects"
package when the "from...import" statement is executed.  (This also
works when "__all__" is defined.)

雖然，有些特定模組的設計，讓你使用 "import *" 時，該模組只會輸出遵循特
定樣式的名稱，但在正式環境 (production) 的程式碼中這仍然被視為是不良習
慣。

記住，使用 "from package import specific_submodule" 不會有任何問題！實
際上，這是推薦用法，除非 import 的模組需要用到的子模組和其他套件的子模
組同名。


6.4.2. 套件內引用
-----------------

When packages are structured into subpackages (as with the "sound"
package in the example), you can use absolute imports to refer to
submodules of siblings packages.  For example, if the module
"sound.filters.vocoder" needs to use the "echo" module in the
"sound.effects" package, it can use "from sound.effects import echo".

You can also write relative imports, with the "from module import
name" form of import statement.  These imports use leading dots to
indicate the current and parent packages involved in the relative
import.  From the "surround" module for example, you might use:

   from . import echo
   from .. import formats
   from ..filters import equalizer

請注意，相對 import 的運作是以目前的模組名稱為依據。因為主模組的名稱永
遠是 ""__main__""，所以如果一個模組預期被用作 Python 應用程式的主模組
，那它必須永遠使用絕對 import。


6.4.3. 多目錄中的套件
---------------------

套件也支援一個特殊屬性 "__path__"。它在初始化時是一個 list，包含該套件
的 "__init__.py" 檔案所在的目錄名稱，初始化時機是在這個檔案的程式碼被
執行之前。這個變數可以被修改，但這樣做會影響將來對套件內的模組和子套件
的搜尋。

雖然這個特色不太常被需要，但它可用於擴充套件中的模組集合。

-[ 註解 ]-

[1] 實際上，函式定義也是「被執行」的「陳述式」；在執行模組階層的函式定
    義時，會將函式名稱加到模組的全域命名空間。
