初學__Python——Python 可重用結構:Python模塊

目錄

一、模塊的基本用法

一、導入模塊

二、編寫一個模塊

二、在哪裏查找模塊

三、是否需要編譯模塊

四、模塊也可獨立運行

五、如何查看模塊提供的函數名


Python中的模塊實際上就是包含函數或者類的 Python 腳本。對於一個大型腳本而言,經常需要將其功能細化,將實現不同功能的代碼放到不同的腳本中實現,在其他的腳本中以模塊的形式使用細化的功能,以便於腳本的維護與重用。

一、模塊的基本用法

模塊是包含函數和其他語句的 Python 腳本文件,它以 ".py" 爲後綴名,也就是 Python 腳本的後綴名。用做模塊的 Python 腳本與其他腳本並沒有什麼區別。

在 Python 中可以通過導入模塊,然後使用模塊中提供的函數或者數據。

一、導入模塊

在 Python 中可以使用以下兩種方法導入模塊或者模塊中的函數。

  • import  模塊名
  • import  模塊名  as  新名字
  • from  模塊名  import  函數名

其中,使用 import 是將整個模塊導入,而使用 from 則是將模塊中某一個函數或者名字導入,而不是整個模塊。使用 import 和 from 導入模塊還有一個不同:要想使用 import 導入模塊中的函數,則必須以模塊名 + "." + 函數名的形式調用函數;而要想使用 from 導入模塊中的函數,則可以直接使用函數名調用,不必在前面加上模塊名稱。

>>> import math  # 使用 import 導入 math 模塊
>>> math.sqrt(9)  # 使用 math 模塊中的 sqrt 函數
3.0
>>> sqrt(9)  # 直接使用 sqrt 名字調用函數——發生錯誤
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'sqrt' is not defined
>>> from math import sqrt  # 使用 from 導入 math 模塊中的 sqrt 函數
>>> sqrt(9)  # # 直接使用 sqrt 名字調用函數——成功使用
3.0

 使用 from 導入模塊中的函數後,使用模塊中的函數會方便得多,不用在調用函數時使用模塊名。如果要想將模塊中所有函數都採用這種方式導入,則可以在 from 中使用 "*" 通配符,表示導入模塊中的所有函數。

>>> from math import sqrt  # 僅從 math 模塊中導入 sqrt
>>> sqrt(9)
3.0
>>> sin(30)  # 調用 math 模塊中的 sin 函數出錯
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'sin' is not defined
>>> from math import *  # 重新從 math 模塊中導入所有函數
>>> sin(30)  # 重新調用 sin 函數
-0.9880316240928618

在 Python 2.x 中,還可以通過使用內置函數 reload 重新載入模塊。reload 可以在模塊被修改的情況下不必關閉 Python 而重新載入模塊。在使用 reload 重載模塊時,該模塊必須已經事先被導入。

在 Python 3 中,reload 函數已經被刪除,要重新載入模塊,則需要使用 importlib(版本舊一點的名稱是 imp ) 模塊中的 reload 函數。

>>> import os              # 導入 os 模塊
>>> import importlib       # 導入 importlib 函數
>>> importlib.reload(os)   # 重新載入 os 模塊
<module 'os' from 'C:\\Users\\圖圖\\AppData\\Local\\Programs\\Python\\Python37-32\\lib\\os.py'>

二、編寫一個模塊

下面是一個簡單的模塊,該模塊中只包含一個函數。這個函數只打印 "I am a module!"。該模塊保存爲 mymodule.py。代碼如下:

def show():   # 聲明 show 函數
    print('I am a module!')

編寫一個調用函數 show 的腳本,將其保存爲 usemodule.py。代碼如下:

import mymodule    # 導入模塊
mymodule.show()    # 調用模塊中的 show 函數

腳本運行後輸出如下:

除了在模塊中聲明函數外,還可以在模塊中定義變量。模塊中的變量同樣可以在其他腳本中使用。以下代碼在 mymodule.py 模塊中添加了一個名爲 name 的變量。

def show():
    print('I am a module!')
name = 'mymodule.py'  # 在模塊中添加一個 name 變量

以下代碼爲修改的 usemodule.py 模塊,其中使用了 mymodule.py 中的 name 變量。

import mymodule
mymodule.show()
print(mymodule.name)      # 打印模塊中的 name 變量
mymodule.name = 'usemodule.py'   # 將 name 變量重新賦值
print(mymodule.name)

腳本運行後輸出如下:

二、在哪裏查找模塊

編寫好的模塊只有被 Python 找到才能被導入。上一節中編寫的模塊和調用模塊的腳本位於同一個目錄中,因此不需要進行設置就能被 Python 找到並導入。如果在該目錄中新建一個 module 目錄,並且把 mymodule.py 轉移到 module 目錄中,再次運行 usemodule.py,則輸出如下:

運行腳本出錯,Python 解釋器沒有找到 mymodule.py 模塊,在導入模塊時,Python 解釋器首先在當前目錄中查找要導入的模塊,如果未找到模塊,則 Python 解釋器會從 sys 模塊中 path 變量指定的目錄中查找導入模塊。如果在以上所有目錄中都未找到導入的模塊,則會輸出出錯信息。

以下代碼是使用 sys.path 輸出 Python 的模塊查找路徑。

>>> import sys
>>> sys.path
['D:\\Python', 
'D:\\Python', 
'C:\\Users\\圖圖\\AppData\\Local\\Programs\\Python\\Python37-32\\python37.zip', 
'C:\\Users\\圖圖\\AppData\\Local\\Programs\\Python\\Python37-32\\DLLs', 
'C:\\Users\\圖圖\\AppData\\Local\\Programs\\Python\\Python37-32\\lib', 
'C:\\Users\\圖圖\\AppData\\Local\\Programs\\Python\\Python37-32', 
'C:\\Users\\圖圖\\AppData\\Local\\Programs\\Python\\Python37-32\\lib\\site-packages', 
'C:\\Program Files\\JetBrains\\PyCharm 2018.3.5\\helpers\\pycharm_matplotlib_backend']

在腳本中可以向 sys.path 添加模塊查找路徑。以下所示腳本中,將當前目錄下的 module 子目錄添加到 sys.path 中,並從 module 目錄中導入 mymodule.py 模塊。代碼如下。

import os
import sys
modulepath = os.getcwd() + '\\module'
sys.path.append(modulepath)
print(sys.path)
import mymodule
mymodule.show()
print(mymodule.name)
mymodule.name = 'usenodule.py'
print(mymodule.name)

運行腳本輸出如下:

['D:\\Python', 
'D:\\Python', 
'C:\\Users\\圖圖\\AppData\\Local\\Programs\\Python\\Python37-32\\python37.zip',
'C:\\Users\\圖圖\\AppData\\Local\\Programs\\Python\\Python37-32\\DLLs', 
'C:\\Users\\圖圖\\AppData\\Local\\Programs\\Python\\Python37-32\\lib', 
'C:\\Users\\圖圖\\AppData\\Local\\Programs\\Python\\Python37-32', 
'C:\\Users\\圖圖\\AppData\\Local\\Programs\\Python\\Python37-32\\lib\\site-packages', 
'C:\\Program Files\\JetBrains\\PyCharm 2018.3.5\\helpers\\pycharm_matplotlib_backend', 
'D:\\Python\\module']
I am a module!
mymodule.py
usenodule.py

從輸出可以看出,當前路徑也被添加到了 sys.path 路徑列表中,這說明 Python 其實是按照 sys.path 中的路徑來查找模塊的。之所以首先在當前目錄中查找,那是因爲 Python 解釋器在運行腳本前將當前目錄添加到 sys.path 路徑列表中了。

三、是否需要編譯模塊

在上面的例子中,運行完 usemodule.py 會發現,module 目錄中除了 mymodule.py 文件外,還多了一個後綴名爲 ".pyc" 文件。其實,該文件就是 Python 將 mymodule.py 編譯成字節碼的文件。雖然 Python 是腳本語言,但 Python 可以將腳本編譯成字節碼的形式。對於模塊,Python 總是在第一次調用後就將其編譯成字節碼的形式,以提高腳本的啓動速度。

Python 在導入模塊時會查找模塊的字節碼文件,如果存在則將編譯後的模塊的修改時間同模塊的修改時間相比較。如果兩者的修改時間不相符,則 Python 將重新編譯模塊,以保證兩者內容相符。被編譯的腳本也是可以直接運行的。

當然,沒有必要去刻意編譯 Python 腳本。不過,由於 Python 是腳本,如果不想將源文件發佈,則可以發佈編譯後的腳本,這樣能起到一定的保護源文件的作用。

對於非模塊的腳本,Python 不會在運行腳本後將其編譯成字節碼的形式。如果想將其編譯,可以使用 commile 模塊。以下代碼可以將 usemodule.py 文件編譯成 ".pyc" 文件。

import py_compile       # 導入 py_compile 模塊
py_compile.compile('usemodule.py','usemodule.pyc')   # 編譯 usemodule.py

運行 compile.py 後,可以看到當前目錄中多了一個 usemodule.pyc 文件。在 Python 3 中,如果在 py_compile.compile 函數中不指定第 2 個參數,則將在當前目錄新建一個名爲 "__pycache__" 的目錄,並在這個目錄中生成 "被編譯模塊名.cpython-32.pyc" 的 pytc 字節碼文件。

運行 usemodule.py 腳本輸出如下:

['D:\\Python', 
'D:\\Python', 
'C:\\Users\\圖圖\\AppData\\Local\\Programs\\Python\\Python37-32\\python37.zip',
'C:\\Users\\圖圖\\AppData\\Local\\Programs\\Python\\Python37-32\\DLLs', 
'C:\\Users\\圖圖\\AppData\\Local\\Programs\\Python\\Python37-32\\lib', 
'C:\\Users\\圖圖\\AppData\\Local\\Programs\\Python\\Python37-32', 
'C:\\Users\\圖圖\\AppData\\Local\\Programs\\Python\\Python37-32\\lib\\site-packages', 
'C:\\Program Files\\JetBrains\\PyCharm 2018.3.5\\helpers\\pycharm_matplotlib_backend', 
'D:\\Python\\module']
I am a module!
mymodule.py
usenodule.py

可以看到其輸出與前面的輸出一樣。編譯後生成的 usemodule.pyc 文件並沒有改變程序功能,只是以 Python 字節碼的形式存在。

另外,還可以通過 Python 的命令行選項將腳本優化編譯。Python 編譯的優化選項有以下兩個:

  • -O      該選項對腳本的優化不多,編譯後的腳本以 ".pyo" 爲擴展名。凡是以 ".pyo" 爲擴展名的 Python 字節碼都是經過優化的。

  • -OO   該選項對腳本優化的程度較大。使用該標誌可以使編譯的 Python 腳本更小。使用該選項可能導致腳本運行錯誤,因此,因謹慎使用。

可以通過在命令行中輸入以下命令將 usemodule.py 優化編譯。

python -O compile.py
python -OO compile.py

四、模塊也可獨立運行

每個 Python 腳本在運行時都有一個 __name__ 屬性(name 前後均是兩條下劃線)。在腳本中通過對 __name__ 屬性值的判斷,可以讓腳本在作爲導入模塊和獨立運行時都可以正確運行。

在 Python 中,如果腳本作爲模塊被導入,則其 __name__ 屬性被設置爲模塊名;如果腳本獨立運行,則其 __name__ 屬性被設置爲 "__main__"。因此可以通過 __name__ 屬性來判斷腳本的運行狀態。

以下所示腳本 mymodule2.py 既可以獨立運行,也可以作爲模塊被其他腳本導入。

# file: mymodule2.py

def show():
    print('I am a module!')
if __name__ == '__main__':
    show()
    print('I am not a module!')

以下所示腳本 usemodule2.py 可以調用 mymodule2.py 模塊

# file: usemodule2.py

import mymodule2
mymodule2.show()
print('my __name__ is', __name__)

運行 usemodule2.py 後,輸出如下:

運行 mymodule2.py 後,輸出如下:

五、如何查看模塊提供的函數名

如果需要獲得導入模塊中所有聲明的名字、函數等,就可以使用內置的函數 dir() 來進行操作。

以下所示代碼將獲得 sys 模塊中的名字和函數。

import sys
print(dir(sys))

輸出結果如下:

['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__', 
'__interactivehook__', '__loader__', '__name__', '__package__', '__spec__', 
'__stderr__', '__stdin__', '__stdout__', '_clear_type_cache', '_current_frames', 
'_debugmallocstats', '_enablelegacywindowsfsencoding', '_framework', '_getframe', 
'_git', '_home', '_xoptions', 'api_version', 'argv', 'base_exec_prefix', 'base_prefix', 
'breakpointhook', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 
'copyright', 'displayhook', 'dllhandle', 'dont_write_bytecode', 'exc_info', 
'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 
'float_repr_style', 'get_asyncgen_hooks', 'get_coroutine_origin_tracking_depth', 
'get_coroutine_wrapper', 'getallocatedblocks', 'getcheckinterval', 'getdefaultencoding', 
'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile', 'getrecursionlimit', 
'getrefcount', 'getsizeof', 'getswitchinterval', 'gettrace', 'getwindowsversion', 
'hash_info', 'hexversion', 'implementation', 'int_info', 'intern', 'is_finalizing', 
'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 
'path_importer_cache', 'platform', 'prefix', 'set_asyncgen_hooks', 
'set_coroutine_origin_tracking_depth', 'set_coroutine_wrapper', 'setcheckinterval', 
'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin', 
'stdout', 'thread_info', 'version', 'version_info', 'warnoptions', 'winver']

dir() 函數的原型如下:

dir( [object] )
  • object        可選參數,要列舉的模塊名。

如果不向 dir() 函數傳遞參數,那麼 dir() 函數將返回當前腳本的所有名字列表。如下所示。

a = [1, 3, 6]  # 定義一個列表
b = 'python'  # 定義一個字符串
print(dir())  # 使用 dir() 函數獲得當前腳本所有名字列表


def fun():  # 定義一個函數
    print('python')


print(dir())  # 再次使用 dir() 函數

兩個 print 的輸出結果如下:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章