我們以前的代碼都是寫在一個(gè)文件中, 而且代碼也比較短.
為了解決上面的這些問題, python 提出了一個(gè)moudle
的概念.
我們每定義一個(gè).py
文件, 其實(shí)就是定義了一個(gè)moudle
.在一個(gè)moudel
中定義的函數(shù),類都可以導(dǎo)入(import
)到另外一個(gè)模塊中, 也可以導(dǎo)入到主模塊(main moudle
)中.
一個(gè)文件就是一個(gè)模塊, 在一個(gè)模塊內(nèi)可以定義變量, 函數(shù), 類等, 也可以有合法的 python 語句.
文件名就是模塊名(不包括擴(kuò)展名.py
). 我們可以通過一個(gè)全局變量__name__
來獲取這個(gè)模塊的名字, 當(dāng)然獲取到的是個(gè)字符串.
定義模塊其實(shí)就是創(chuàng)建一個(gè).py
文件.
python之父建議的的模塊命名:
_
連接_
開頭, 其余與公共模塊一樣.第 1 步: 創(chuàng)建一個(gè)文件fibo.py
, 相當(dāng)于創(chuàng)建了一個(gè)模塊fibo
.
def fib(n): #1,1,2,3,5,8,13,21,34... a, b = 0, 1 for i in range(n): print(b, end=" ") a, b = b, a + b #print()
第 2 部: 再創(chuàng)建一個(gè)模塊demo
, 作為我們程序的入口, 這樣的模塊其實(shí)就是main moudle
. 在主模塊內(nèi)部使用我們第 1 步創(chuàng)建的模塊.
import fibo # 導(dǎo)入需要的模塊print(__name__) # 當(dāng)前模塊直接執(zhí)行, 所以當(dāng)前模塊就是主模塊 輸出__main__# 模塊名就成為了在 fibo 這個(gè)模塊中定義的全局變量, 函數(shù), 類的命名空間print(fibo.__name__) # 輸出模塊名# 調(diào)用模塊內(nèi)定義的函數(shù)fibo.fib(20)
import
加載模塊的位置. **模塊名.成員
的方式來使用模塊內(nèi)定義的成員:全局變量, 函數(shù), 類模塊名.類名
的方式來使用.比如模塊a
中定義了一個(gè)類Person
, 則創(chuàng)建對(duì)象的方式:p = a.Person()
導(dǎo)入模塊之后, 可能模塊名比較長(zhǎng), 想換個(gè)短點(diǎn)的, 我們可以給導(dǎo)入的模塊起個(gè)別名.
import hello_world as hwhw.foo()
注意:
起別命名之后只會(huì)創(chuàng)建hw
命名空間, 而不會(huì)再創(chuàng)建hello_world
命名空間. 所以這樣使用是錯(cuò)誤的:hello_world.foo()
前面我們已經(jīng)了解到使用import
可以導(dǎo)入一個(gè)模塊.
我們也可以多次使用import
來導(dǎo)入多個(gè)模塊
import aimport bimport c
上面這種寫法比較啰嗦, 可以使用一個(gè)import
同時(shí)導(dǎo)入多個(gè)模塊
import a, b, c
from
方式導(dǎo)入)單獨(dú)使用import
是導(dǎo)入整個(gè)模塊, 默認(rèn)的所有定義都會(huì)導(dǎo)入, 而且會(huì)創(chuàng)建新的命名空間. 并沒模塊的代碼也會(huì)執(zhí)行.
如果我們僅僅是用到模塊中某個(gè)函數(shù)或者類, 這個(gè)時(shí)候, 我們可以只導(dǎo)入我們想要的某個(gè)定義, 而不需要導(dǎo)入整個(gè)模塊.
語法:
from 模塊 import 具體的定義
from fibo import fib # 從模塊 fibo 中只導(dǎo)入 fibfib(20)
說明:
from
語句導(dǎo)入的時(shí)候, 并不會(huì)創(chuàng)建新的命名空間, 而是把導(dǎo)入的定義放在了當(dāng)前命名空間中, 所以使用的時(shí)候不需要添加命名空間.fibo.fib(20)
, 因?yàn)楦揪筒淮嬖?code>fibo這個(gè)命名空間from fibo import fib, a, b, c
from fibo import *
from
導(dǎo)入的作用域問題把一個(gè)函數(shù)從一個(gè)模塊導(dǎo)入當(dāng)前模塊時(shí), 并不會(huì)改變這個(gè)函數(shù)的作用域規(guī)則. 也就是說好函數(shù)的全局命名空間仍然是那個(gè)函數(shù)定義所在的命名空間.
a.py:
n = 30def foo(): print("a模塊: n的值" + str(n))
b.py:
from a import foo, nn = 1000foo()print("當(dāng)前模塊: n 的值:" + str(n))#print("當(dāng)前模塊:n的值:",n)#print("當(dāng)前模塊:n的值:%d"%n)
原因分析:
foo
函數(shù)中的n
, 仍然是a
模塊中的n
為什么呢?
在 python 中任何數(shù)據(jù)都是變量, n
僅僅是對(duì)象的一個(gè)引用(符號(hào)). 導(dǎo)入 n
到當(dāng)前模塊, 僅僅是在原來的對(duì)象上多了一個(gè)引用而已.
這個(gè)兩個(gè)n
由于命名空間不同, 所以不是同一個(gè)n
. 修改一個(gè)變量的值, 其實(shí)是讓這個(gè)變量指向了一個(gè)新的對(duì)象
導(dǎo)入成功from a import foo, n
之后是這樣的:
在 b 模塊中修改 n 的值n = 1000
之后是這樣的:
對(duì)模塊的運(yùn)行方式做個(gè)總結(jié).
到目前為止, 我們有兩種方式去運(yùn)行模塊:
python 模塊名.py
. 這種模塊為主模塊, 這樣運(yùn)行方式叫做以程序的方式運(yùn)行.import
語句的方式來運(yùn)行模塊.我們可以通過__name__
屬性的值來判斷這個(gè)模塊的運(yùn)行方式.
if __name__ == "__main__": # 是 程序的方式運(yùn)行 passelse: # 否 以導(dǎo)入的方式運(yùn)行 pass
當(dāng)我們?nèi)ゼ虞d一個(gè)模塊的時(shí)候, python 會(huì)在指定的路徑中搜索這個(gè)模塊, 一旦搜索到則會(huì)立即導(dǎo)入. 如果搜索不到則會(huì)拋出異常.
出現(xiàn)下面這種情況一般是模塊的路徑不對(duì)導(dǎo)致的!
python 搜索模塊按照一定的順序來搜索的:
搜索路徑存放在sys
的path
變量中.
兩種更方式:
sys.path
是一個(gè)列表, 只需要在里面添加上你需要的路徑即可.
sys.path.append("路徑")
設(shè)置環(huán)境變量PYTHONPATH
, 在這個(gè)環(huán)境變量中設(shè)置你需要的路徑即可. 只添加自己的路徑即可, python 自己本身的搜索路徑不受影響
一個(gè).py
文件就是一個(gè)模塊, 但是模塊一多, 管理起來也是比較麻煩.
python 提供包是為了更好的對(duì)模塊進(jìn)行管理.
包可以看做是一組模塊的集合, 把這組模塊放在一個(gè)包的名稱下.包這項(xiàng)技術(shù)可以解決不同的應(yīng)用程序之間模塊的命名沖突問題.
包包括兩個(gè)必要條件:
__init__.py
的初始化文件.注意:
一個(gè)包中可以有自己的子包, 子包也可以再有子包... 每一個(gè)包都會(huì)有一個(gè)自己的__init__.py
文件.
定義包是為了更好的管理模塊, 有幾種導(dǎo)入模塊的方式!
假設(shè)有這樣的一個(gè)包結(jié)構(gòu):
import
導(dǎo)入模塊# 導(dǎo)入 echo 模塊import sound.effects.echo# 使用的時(shí)候必須使用全名sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
from
導(dǎo)入模塊# 導(dǎo)入 echo 模塊from sound.effects import echo# 只需要包名即可echo.echofilter(input, output, delay=0.7, atten=4)
from
直接導(dǎo)入模塊中的定義# 從模塊中導(dǎo)入定義from sound.effects.echo import echofilter# 直接調(diào)用導(dǎo)入的函數(shù)即可echofilter(input, output, delay=0.7, atten=4)
當(dāng)我們多個(gè)模塊中都導(dǎo)入同一個(gè)模塊的時(shí)候, 只有第一次導(dǎo)入的時(shí)候才會(huì)執(zhí)行包的__init__()
和模塊中的代碼.
可以這樣理解: 當(dāng)導(dǎo)入某個(gè)模塊的時(shí)候, 會(huì)先查找某個(gè)模塊是否已經(jīng)導(dǎo)過, 如果已經(jīng)導(dǎo)入過, 就直接使用不再重新導(dǎo)入.
*
通配符導(dǎo)入使用from 包 import *
, 可以導(dǎo)入整個(gè)包下所有的模塊.
但是由于各個(gè)操作系統(tǒng)的在對(duì)文件名的命名上的差異, 默認(rèn)情況下這個(gè)語句一個(gè)包都導(dǎo)入不了.
我們可以在這個(gè)包的__init__.py
定義一個(gè)列表, 這個(gè)列表中定義使用*
的時(shí)候可以導(dǎo)入哪些包
__all__ = ["a", "b"] # 使用 * 的時(shí)候?qū)隺 和 b 兩個(gè)包
同一個(gè)包下的模塊互相導(dǎo)入的時(shí)候, 也需要些完全限定名, 也就是把他們的上級(jí)的所有的包都要寫上, 這是比較麻煩的.
所以, python 還是支持一種相對(duì)導(dǎo)入.
一個(gè)點(diǎn).
表示當(dāng)前包, 兩個(gè)點(diǎn)..
表示表示父包
相對(duì)導(dǎo)包只能用在from
中.
from . import my2
注意:
相對(duì)導(dǎo)包的原理是根據(jù)當(dāng)前模塊的__name__
來計(jì)算的包的路徑, 由于主模塊的__name__
值永遠(yuǎn)是__main__
, 所以主模塊中不能使用相對(duì)導(dǎo)包, 只能使用絕對(duì)路徑去導(dǎo)包.
有了包之后, 模塊都應(yīng)該放入到相應(yīng)的包下
聯(lián)系客服