目錄 | 上一節 (3.3 錯誤檢查) | 下一節 (3.5 主模組)
3.4 模組
本節介紹模組的概念以及如何使用跨多個檔案的函式。
模組和匯入
任何一個 Python 原始檔都是一個模組。
# foo.py
def grok(a):
...
def spam(b):
...
import
語句載入並執行一個模組。
# program.py
import foo
a = foo.grok(2)
b = foo.spam('Hello')
...
名稱空間
模組是命名值的集合,有時也稱為名稱空間。名稱是原始檔中定義的所有全域性變數和函式。匯入之後,模組名稱用作字首。因此,稱為名稱空間。
import foo
a = foo.grok(2)
b = foo.spam('Hello')
...
模組名直接繫結到檔名(foo -> foo.py)。
全域性定義
填充模組名稱空間的內容是定義在全域性(global)作用域中任何內容。考慮定義了相同變數 x 的兩個模組。
# foo.py
x = 42
def grok(a):
...
# bar.py
x = 37
def spam(a):
...
在本例中,x
指向不同的變數。一個是 foo.x
,另一個是 bar.x
。不同的模組可以使用相同的名稱並且這些名稱不會相互衝突。
模組是隔離的。
把模組當做環境
對於所有定義在模組裡面的程式碼而言,模組構成一個封閉的環境。
# foo.py
x = 42
def grok(a):
print(x)
全域性變數始終繫結到封閉模組(相同檔案),每個原始檔都是它自己的小宇宙。
模組執行
匯入模組時,模組中的所有語句依次執行(execute),直到到達檔案末尾。模組名稱空間的內容是所有的全域性名稱,這些名稱在執行過程結束時仍然被定義。如果有指令碼語句在全域性作用域中執行任務(如列印,建立檔案等),您將看到它們在匯入模組時執行。
import as
語句
可以在匯入模組時更改其名稱:
import math as m
def rectangular(r, theta):
x = r * m.cos(theta)
y = r * m.sin(theta)
return x, y
它的作用與普通匯入相同,僅僅是重新命名模組而已。
from import
語句
from import
語句從模組中選出符號並使它們在區域性可訪問。
from math import sin, cos
def rectangular(r, theta):
x = r * cos(theta)
y = r * sin(theta)
return x, y
這允許使用模組的某些部分,而不必輸入模組字首。對於經常使用的名稱,這非常有用。
匯入說明
有關匯入的各種變化不改變模組的工作方式。
import math
# vs
import math as m
# vs
from math import cos, sin
...
具體來說,import
始終執行整個檔案並且模組仍然是隔離的環境。
import module as
語句只區域性地更改名稱。在後臺,from math import cos, sin
語句仍載入全部的數學模組。當匯入完成後,它僅僅將模組中的 cos
和 sin
名稱複製到區域性名稱空間中。
模組載入
每個模組僅載入和執行一次。注意:重複匯入僅返回先前所載入模組的引用
sys.modules
是所有已載入模組的字典。
>>> import sys
>>> sys.modules.keys()
['copy_reg', '__main__', 'site', '__builtin__', 'encodings', 'encodings.encodings', 'posixpath', ...]
>>>
注意:當修改模組的原始碼後,如果重複import
語句會產生一個常見的困惑。由於模組快取 sys.modules
,重複匯入總是返回之前載入的模組——即使更改已經發生。將修改後的程式碼載入到 Python 中最安全的方式是退出然後重啟直譯器。
定位模組
搜尋模組時,Python 從路徑列表(sys.path)中查詢。
>>> import sys
>>> sys.path
[
'',
'/usr/local/lib/python36/python36.zip',
'/usr/local/lib/python36',
...
]
當前工作目錄通常是第一個。
模組搜尋路徑
如前所述,sys.path
包含搜尋路徑。可以根據需要手動調整 。
import sys
sys.path.append('/project/foo/pyfiles')
也可以通過環境變數新增搜尋路徑。
% env PYTHONPATH=/project/foo/pyfiles python3
Python 3.6.0 (default, Feb 3 2017, 05:53:21)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.38)]
>>> import sys
>>> sys.path
['','/project/foo/pyfiles', ...]
在大部分情況下,沒必要手動調整模組搜尋路徑。但是,如果嘗試匯入的 Python 程式碼位於特殊位置,或者無法從當前工作目錄輕鬆訪問,那麼就需要手動調整搜尋路徑了。
練習
因為本練習涉及模組,所以確保在適當的環境中執行 Python 至關重要。模組經常給程式設計新手帶來問題,這些問題與當前工作目錄相關或者與 Python 路徑設定相關。對於本課程,假定您是在 Work/
目錄下編寫所有的程式碼。為了獲得最佳結果,應該確保也是在 Work/
目錄下執行直譯器。否則,需要確保 practical-python/Work
已新增到 sys.path
。
練習 3.11:模組匯入
在第 3 節中,我們建立了一個通用目標函式 parse_csv()
用於解析 CSV 資料檔案的內容。
現在,我們來看看如何在其它程式中使用該函式。首先,啟動一個新的 shell 視窗,進入到放置所有檔案的目錄中。我們將要匯入它們。
啟動 Python 互動模式。
bash % python3
Python 3.6.1 (v3.6.1:69c0db5050, Mar 21 2017, 01:21:04)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
當Python 互動模式啟動後,嘗試匯入某些之前編寫的程式。應該能看到輸出和以前一樣。強調一下,匯入模組會執行模組中的程式碼。
>>> import bounce
... watch output ...
>>> import mortgage
... watch output ...
>>> import report
... watch output ...
>>>
如果沒有程式碼執行,可能是因為在錯誤的目錄下執行了 Python。現在,嘗試匯入 fileparse
模組並獲取有關該模組的幫助。
>>> import fileparse
>>> help(fileparse)
... look at the output ...
>>> dir(fileparse)
... look at the output ...
>>>
嘗試使用 fileparse
模組來讀取一些資料:
>>> portfolio = fileparse.parse_csv('Data/portfolio.csv',select=['name','shares','price'], types=[str,int,float])
>>> portfolio
... look at the output ...
>>> pricelist = fileparse.parse_csv('Data/prices.csv',types=[str,float], has_headers=False)
>>> pricelist
... look at the output ...
>>> prices = dict(pricelist)
>>> prices
... look at the output ...
>>> prices['IBM']
106.11
>>>
嘗試匯入一個函式,以便不用再包含模組名:
>>> from fileparse import parse_csv
>>> portfolio = parse_csv('Data/portfolio.csv', select=['name','shares','price'], types=[str,int,float])
>>> portfolio
... look at the output ...
>>>
練習 3.12:使用庫模組
在第 2 節中,編寫了 report.py
程式用來生成像下面這樣的股票報告:
Name Shares Price Change
---------- ---------- ---------- ----------
AA 100 9.22 -22.98
IBM 50 106.28 15.18
CAT 150 35.46 -47.98
MSFT 200 20.89 -30.34
GE 95 13.48 -26.89
MSFT 50 20.89 -44.21
IBM 100 106.28 35.84
使用該程式並對其進行修改,以便使用 fileparse
模組中的函式完成所有輸入檔案的處理。為此,將 fileparse
作為模組匯入,並修改 read_portfolio()
和 read_prices()
函式以便使用 parse_csv()
函式。
在本練習開始時,請使用互動示例作為指南。之後,應該能夠獲得與之前完全相同的輸出。
練習 3.14:使用更多的庫匯入
在第 1 節中,編寫了一個讀取股票投資組合和計算費用的程式 pcost.py
。
>>> import pcost
>>> pcost.portfolio_cost('Data/portfolio.csv')
44671.15
>>>
請修改 pcost.py
檔案,以便它能夠使用 report.read_portfolio()
函式。
說明
當完成練習後,您應該擁有三個程式。包含通用目的函式 parse_csv()
的fileparse.py
程式。用於生成報告,且包含 read_portfolio()
和 read_prices()
函式的 report.py
程式。最後,利用 report.py
程式中編寫的read_portfolio()
函式去計算股票投資組合費用的 pcost.py
程式。