在之前的程序裡,你可能用過類似from math import pi
的語句來導入變量或函數。這其實就是在使用模塊。
模塊:Python將程序存入一個文件,可在解釋器中運行。這個文件就是模塊。
模塊中的語句可以導入(import)到其他Python程序中。
使用模塊的好處:模塊化,每個模塊寫相關的功能;避免文件過長和混亂。
新建一個文件(文件名為模塊名.py
),在文件裡寫程序。
舉個例子:創建一個和三角形相關的模塊:
新建文件tri_f.py
,然後在裡面寫入:
import math
print("tri_f 模塊 開始")
def edge_length(a, b, c):
"""返回三邊長度之和"""
return a + b + c
def area(a, b, c):
"""返回三角形面積"""
p = (a + b + c) / 2
s = math.sqrt(p * (p - a) * (p - b) * (p - c))
return s
print("tri_f 模塊 結束")
然後保存(Ctrl+s)。這樣模塊就創建好了。
使用模塊通過import
語句導入模塊中的變量定義、函數等可執行語句等。
例,使用模塊tri.f
。
在tri_f.py
同級目錄下創建test.py
。
注意:如果不是同級目錄,解釋器會找不到,需要在sys.path中添加查找路徑,如
import sys sys.path.append(r'D:\PY_TEST\pythonProject\6modules')
#替換為自己的目錄
a, b, c = 3, 4, 5
# 從模塊導入特定函數
from tri_f import area # 從tri_f模塊導入函數area的定義
print(area(a, b, c)) # 然後就可以使用函數area,計算三角形面積了
# 導入模塊
import tri_f #導入 tri_f模塊
print(tri_f.area(a, b, c)) # 函數前面需要加上模塊名
# 給模塊起別名
import tri_f as tr
print(tr.area(a, b, c))
# (不推薦)從模塊導入全部函數(_開頭的函數除外)
from tri_f import *
print(area(a, b, c))
print(edge_length(a, b, c))
運行test.py我們得到下面結果:
tri_f 模塊 開始
tri_f 模塊 結束
6.0
6.0
6.0
6.0
12.0
通過import模塊 ,我們不僅導入了函數,還會執行模塊中的語句。
總結一下,Import的用法:
# 從模塊導入特定函數
from 模塊 import 函數
# 導入模塊
import 模塊
# 給模塊起別名
import 模塊 as 別名
# (不推薦)從模塊導入全部函數(_開頭的函數除外)及變量
from 模塊 import *
__name__
你可能看到過 下面的語句:
if __name__ == '__main__':
print('hello')
這裡的__name__
實際上就是模塊的名字。模塊被導入時,__name__
是模塊的文件名。當這個模塊作為主程序運行時,模塊的__name__
會賦值為'__main__'
。
import math
print('name=',__name__)
def edge_length(a, b, c):
"""返回三邊長度之和"""
return a + b + c
def area(a, b, c):
"""返回三角形面積"""
p = (a + b + c) / 2
s = math.sqrt(p * (p - a) * (p - b) * (p - c))
return s
if __name__ == '__main__':
print(area(3, 4, 5))
print('hello')
運行tri_f.py,輸出:
name= __main__
6.0
hello
運行test.py時,tri_f中的__name__
就會變成模塊名 tri_f
,所以不會執行tri.f模塊中if裡的內容:
if __name__ == '__main__':
print(area(3, 4, 5))
print('hello')
import語句導入模塊時發生的事情:
執行模塊中語句(包括定義函數等)。
注意:
!模塊中的語句用於初始化模塊,且僅在 import 語句 第一次 遇到模塊名時執行(防止重復)。
!需要注意,如果有多個相同的函數名,最後定義的函數會把之前的同名函數給覆蓋掉。
!可以用與訪問模塊函數一樣的標記法,訪問模塊的全局變量,modname.itemname
。
模塊有自己的私有符號表,用作模塊中所有函數的全局符號表。因此,模塊內全局變量不會與用戶定義的全局變量發生沖突。(別糾結,命名空間在第九章類會詳細說)
包是裝著模塊的文件夾,文件夾下必須含 __init__.py
文件。
最簡情況下,__init__.py
只是一個空文件,但該文件也可以執行包的初始化代碼,或設置 __all__
變量,詳見下文(從包中導入*)。
sound/ #頂層包
__init__.py #初始化
formats/ #子包,用於格式化聲音
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/ #子包,用於聲音特效
__init__.py
echo.py
surround.py
reverse.py
...
filters/ #子包,用於過濾
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
從包導入與從模塊導入非常類似。
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 sound.effects.echo import echofilter
同樣,這樣也會加載子模塊 echo
,但可以直接使用函數 echofilter()
:
echofilter(input, output, delay=0.7, atten=4)
注意,使用 from package import item
時,item 可以是包的子模塊(或子包),也可以是包中定義的函數、類或變量等其他名稱。
優先查找包中定義的函數、類或變量等,未找到則假定 item 是模塊,並嘗試加載模塊。如果仍然找不到 item,則觸發 ImportError
異常。
相反,使用 import item.subitem.subsubitem
句法時,除最後一項外,每個 item 都必須是包;最後一項可以是模塊或包,但不能是上一項中定義的類、函數或變量。
類似模塊導入*,使用 from sound.effects import *
時,該語句應該導入包的所有子模塊。但是這可能會導入太多東西, 浪費時間且造成沖突。因此,使用 from sound.effects import *
只會導入在__init__.py
中__all__
變量裡的模塊。
__all__ = ["echo", "surround", "reverse"]
包中含有多個子包時還可以用 import 語句的 from module import name
形式執行相對導入。這些導入語句使用前導句點表示相對導入中的當前包和父包。例如,相對於 effect
包下的surround
模塊,可以使用:
from . import echo
from .. import formats
from ..filters import equalizer