跳至內容

用戶:Xyy23330121/Python/模組

來自維基學院


我們之前接觸過 import 語句以導入模塊。本章將詳細講解 import 語句的工作方式,如何編寫模塊,以及如何安裝第三方模塊。

簡單模塊

[編輯 | 編輯原始碼]

任何 .py 的文件都是一個簡單的模塊。為簡單起見,我們新建一個文件夾,在其中放兩個文件:

folder/
    MyModule.py
    test.py

其中 MyModule.py 的內容為:

#!/usr/bin/python
# -*- coding: utf-8 -*-

var = "变量"

def f(): print("函数")

class C:
	def __str__(self): return "类"

test.py 的內容為:

#!/usr/bin/python
# -*- coding: utf-8 -*-

import MyModule

print(MyModule.var)
MyModule.f()
obj = MyModule.C()
print(obj)

運行 test.py,輸出為:

变量
函数
类

可見,我們可以正常導入之前已經寫過的文件當中的變量、函數以及類。而 MyModule.py 就已經是一個模塊了。

拓展:module 對象

[編輯 | 編輯原始碼]

模塊同類、函數、一般的變量一樣,它也是一個對象。對於該對象的具體操作暫不贅述,這裏僅作為拓展。

簡單包

[編輯 | 編輯原始碼]

如果一個文件夾具有 __init__.py 文件,則該文件夾會被視為一個模塊(包)。

提示
對於文件系統中的體現,也不是沒有例外,命名空間包就不需要子包一定在父包的目錄下。但這是一種較高級的特性了,這裏不多涉及這一種包。

就像維基學院的頁面可以一個頁面有幾個分頁面一樣,模塊的包也可以一個包包含幾個子包。父包和子包在文件系統中的體現,就是父包是帶有 __init__.py 的父文件夾,而子包是帶有 __init__.py 的、父包文件夾的子文件夾。

這裏我們執行以下步驟以創建一個簡單的包。

  1. 把上面「簡單模塊」章節中的 MyModule.py 重命名為 __init__.py
  2. 新建一個 MyModule 文件夾,把 __init__.py 移動進去,形成這樣的結構:
folder/
    test.py
    MyModule/
        __init__.py

我們運行 test.py,輸出和上面的「簡單模塊」章節中的內容是一致的。

import 語句與包

[編輯 | 編輯原始碼]

我們依舊使用上面的示例。如果我們需要導入tkinterttk 子模塊時,應當這樣導入:

import tkinter.ttk

但是,如果用上面的方法進行導入。我們在使用包的內容時,則需要用以下方式,寫明包的全稱來調用包中的東西,比如:

tkinter.ttk.Label(text = "test")

我們有兩種解決方法。第一種是 import ... as ...,第二種是利用 from ... import ...

from ... import ...

[編輯 | 編輯原始碼]

類似 from math import trunc 可以讓之後調用 math.trunc 時無需標註包名,直接用 trunc 即可。相對調用也可以用於省略父包的名稱。比如:

from tkinter import ttk
ttk.Label(text = "test")  #相比上面的调用,省略了"tkinter."

這對於前綴過長時十分好用。

相對導入

[編輯 | 編輯原始碼]

對於包中的內容而言,支持一種稱作「相對導入」的方式。我們從文件樹開始。

folder/
    __init__.py
    test.py
    MyModule/
        __init__.py
        test2.py
        test3.py
        SubModule/
            __init__.py
            test4.py
    MyModule2/
        __init__.py
        test5.py

此時,在 test2.py 中支持以下操作:

from . import test3               #导入test3.py
from .. import test               #导入test.py
from .SubModule import test4     #导入test4.py
from ..MyModule2 import test5    #导入test5.py

注意:模塊的名稱

[編輯 | 編輯原始碼]

相對導入是關乎模塊名稱的。我們在導入模塊後,可以用 __name__ 屬性來訪問模塊的名稱,比如:

import tkinter.ttk as ttk
print(ttk.__name__)  #输出:tkinter.ttk

而直接被執行的(而非被導入)的文件總會以'__main__' 為名稱。比如我們直接執行以下代碼:

print(__name__)  #输出:__main__

相對導入和文件的 __name__ 屬性有關。在導入時,會按照被導入文件的 __name__ 屬性決定相對導入的對象。之所以上面在 test2.py 中的操作可以成功,是因為我們先執行了:

import folder.MyModule.test2

此時,test2.py__name__ 屬性為 folder.MyModule.test2。在導入 test2.py 中代碼時,test2.py 中的內容會按照 test2.py__name__ 屬性,被解釋為:

from . import test3               #"." 被替换为 "folder.MyModule."
from .. import test               #".." 被替换为 "folder."
from .SubModule import test4     #"." 被替换为 "folder.MyModule."
from ..MyModule2 import test5    #".." 被替换为 "folder."

若直接運行 test2.pytest2.py__name__ 屬性變為了 __main__,此時就不適用於相對導入。使用了相對導入的文件不應該被直接執行。

特別的,對於 folder/__init__.py ,直接用:

from MyModule import test2

也是相對導入。

由於上面的特性,我們可以通過 __name__ 來判斷文件是否被直接執行。比如:

if __name__ == "__main__":
    print("被直接执行")
else:
    print("不被直接执行")

此時,可以通過不同的運行狀況,來使用對應的策略。比如:

def times(a,b):
    return a*b

if __name__ == "__main__":
    print(
     times(
      float(input("输入两个数字,输出乘法结果。\n第一个数字:")),
      float(input("第二个数字:"))
     ))

直接執行包

[編輯 | 編輯原始碼]

對於僅有單個 .py 文件組成的模塊,直接執行模塊就是直接執行該 .py 文件。而對於包而言,直接執行模塊時,會執行其目錄中的 __main__.py 文件。

讀者既可以打開包的位置並手動執行其中的 __main__.py,也可以在比如 cmd.exe 中使用以下指令,直接執行已被安裝的包:

python -m PackageName

如果一個包中沒有 __main__.py,則那個包就不應被直接執行。

導入 __main__

[編輯 | 編輯原始碼]

我們也可以從模塊中導入 __main__ 的內容。比如:

folder/
    importmain.py
    test.py

其中 importmain.py 的內容為:

#!/usr/bin/python
# -*- coding: utf-8 -*-

import __main__

def prtvar: print(__main__.var)

test.py 的內容為:

#!/usr/bin/python
# -*- coding: utf-8 -*-

import importmain

var = "__main__中的变量"
importmain.prtvar()

運行 test.py,輸出為:

__main__中的变量

可以看出,我們在模塊中反過來調用了當時直接運行的文件中的變量。每個直接運行的文件,在運行中,可以被視為以 __main__ 為名稱的模塊。

模塊參數

[編輯 | 編輯原始碼]

在模塊中,一些特殊的變量在運行時可能有特殊的效果。如果模塊是包,這些變量應該寫在 __init__.py 里。

from Module import * 時,導入的內容名稱列表。比如在 json 模塊中,有:

__all__ = [
    'dump', 'dumps', 'load', 'loads',
    'JSONDecoder', 'JSONDecodeError', 'JSONEncoder',
]

如果我們使用:

from json import *

相當於:

from json import dump, dumps, load, loads, JSONDecoder, JSONDecodeError, JSONEncoder

如果不設置此項,則默認會導入所有對象(如果是包,還會導入所有子模塊),這會導致運行時間延長,並可能導致一些其它的副作用。

其它自定義參數

[編輯 | 編輯原始碼]

還有一些其它參數可以使用。這些變量在導入時作用不大,但在一些情況下可能有幫助。

參數名 類型 解釋
__version__ 字符串 表示模塊版本的字符串
__author__ 字符串 表示模塊作者,經常還包含作者的電子郵箱。比如
'Sample Name <Sample@example.com>'

導入時參數

[編輯 | 編輯原始碼]

上面 Python 文檔中所示屬性會在導入時自動寫入,並可以被調用。

import 語句查找模塊的順序

[編輯 | 編輯原始碼]

如果每創建一個新項目,都要把之前寫的模塊複製到項目文件夾下,無疑是很麻煩的。我們之前導入 keyword 模塊時,也沒有在項目文件夾中創建 keyword.py。可見,導入模塊不需要把模塊文件放在項目文件夾下。

對於一個語句 import Module,Python 會按先後順序查詢以下內容。

模塊緩存

[編輯 | 編輯原始碼]

在運行 Python 時,會先查詢已有的模塊緩存 sys.modules。該緩存保存了已經調用的所有模塊的信息。如果新調用的模塊恰好在這些信息內,則會直接從該信息調用新模塊。

比如,如果之前導入過 MyModule.SubModule ,則 MyModuleMyModule.SubModule 都會被寫入緩存中。

使用查找器

[編輯 | 編輯原始碼]

如果在緩存中找不到,則會使用查找器來進一步查找。讀者無需詳細了解查找器的內容,簡單來講,查找器會查找:

  1. 內置模塊
    比如 keywordmath等。
  2. 文件路徑
    當前運行的文件,其所在文件夾中是否有對應模塊。
  3. 安裝的模塊路徑
    已經安裝的模塊所在的路徑。

已經安裝的模塊所在路徑

[編輯 | 編輯原始碼]

以Windows系統為例,默認是安裝在 %AppData%\Python\Python312\site-packages

安裝第三方模塊

[編輯 | 編輯原始碼]

除 Python 的內置模塊、讀者自己撰寫的模塊之外。Python 社區還提供了許多第三方模塊。

我們一般使用 pip 來安裝第三方模塊。pip 是 Python 附帶的包管理器,我們可以在 cmd.exe 或其它類似的命令行程序中,輸入以下的內容:

pip install numpy

並按回車,以安裝第三方的 numpy 模塊。

搜尋第三方庫

[編輯 | 編輯原始碼]

在安裝之前,讀者應當先了解到第三方庫的具體內容。比如我們希望能找到更快地、進行數字計算的方式,具體有以下幾類方法:

  • 搜尋引擎:在搜尋引擎上搜索形如「Python 高效數字計算」的內容。
    如果使用谷歌等外國搜尋引擎,還可以搜索這些關鍵詞的英文版本。
  • 第三方社區:在 Stack Overflow 或 zhihu 等問答網站上,搜索類似的關鍵詞。
  • Python官方的指引:前往 https://www.python.org/ 找到 Use Python for… 章節,並查看其內容。

在搜尋時,要重點關注第三方庫的文檔。文檔會對安裝和使用方法有較為詳細的介紹。有的文檔甚至附帶使用教程。

解決安裝疑難

[編輯 | 編輯原始碼]

由於中國大陸的網絡環境過於「良好」,讀者可能出現下載超時等情況導致安裝失敗。為此,有時需要添加額外的安裝參數。

通過中國大陸的來源安裝

[編輯 | 編輯原始碼]

中國大陸內部有許多組織會在自己的伺服器上「託管」一些常用的 Python 包。以清華大學為例,我們可以嘗試使用以下的方式,從清華大學的伺服器下載並安裝 numpy:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple numpy

通過代理安裝

[編輯 | 編輯原始碼]

如果讀者可以通過代理伺服器來訪問互聯網,則可以使用以下方法:

pip install --proxy 127.0.0.1:1080 numpy

這裏的代理伺服器設置為 127.0.0.1:1080。讀者應當隨着自己的代理伺服器情況自行調整設置。

調整超時限制

[編輯 | 編輯原始碼]

默認情況下,下載伺服器在 15 秒內沒有響應就會超時。我們可以添加一些參數來修改超時時間:

pip install --timeout 99999 numpy

這裏設置為 99999 秒。

組合使用

[編輯 | 編輯原始碼]

以上的參數可以結合起來使用。比如:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --timeout 99999 numpy

其它疑難雜症

[編輯 | 編輯原始碼]

對於其它的疑難雜症,我們可以輸入以下內容,查看使用幫助:

pip help

學習第三方模塊的使用方法

[編輯 | 編輯原始碼]

第三方模塊的文檔許多都是英文,如果讀者有足夠的英文功底,或者能使用翻譯軟件,可以嘗試直接閱讀第三方模塊的官方文檔。如果使用翻譯軟件,最好是對照着中英文來閱讀,因為翻譯可能不準確。

如果沒有足夠的英文功底,讀者就需要到網站上,找到類似本資料的中文學習資料。此時讀者已經經過「搜尋第三方庫」找到了第三方庫的名稱,可以直接上搜尋引擎搜索比如「Python numpy」的內容,社區會為讀者提供幫助的。

如果有照着教程或文檔使用還不了解的內容,讀者可以選擇去 Stack Overflow 等問答網站上求助,也可以自己做一些測試。之前章節中 這個頁面 就是本資料作者進行的測試。