08-01模塊-創(chuàng)新互聯(lián)

一 模塊介紹

在Python中,一個py文件就是一個模塊,文件名為xxx.py模塊名則是xxx,導(dǎo)入模塊可以引用模塊中已經(jīng)寫好的功能。如果把開發(fā)程序比喻成制造一臺電腦,編寫模塊就像是在制造電腦的零部件,準(zhǔn)備好零部件后,剩下的工作就是按照邏輯把它們組裝到一起。

我們提供的服務(wù)有:成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站、微信公眾號開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、梅江ssl等。為上千家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的梅江網(wǎng)站制作公司

將程序模塊化會使得程序的組織結(jié)構(gòu)清晰,維護(hù)起來更加方便。比起直接開發(fā)一個完整的程序,單獨(dú)開發(fā)一個小的模塊也會更加簡單,并且程序中的模塊與電腦中的零部件稍微不同的是:程序中的模塊可以被重復(fù)使用。所以總結(jié)下來,使用模塊既保證了代碼的重用性,又增強(qiáng)了程序的結(jié)構(gòu)性和可維護(hù)性。另外除了自定義模塊外,我們還可以導(dǎo)入使用內(nèi)置或第三方模塊提供的現(xiàn)成功能,這種“拿來主義”極大地提高了程序員的開發(fā)效率。

插圖:惡搞圖01
08-01 模塊

二 模塊的使用

2.1 import語句

有如下示范文件

#文件名:foo.py
x=1
def get():
    print(x)
def change():
    global x
    x=0
class Foo:
    def func(self):
       print('from the func')

要想在另外一個py文件中引用foo.py中的功能,需要使用import foo,首次導(dǎo)入模塊會做三件事:

1、執(zhí)行源文件代碼

2、產(chǎn)生一個新的名稱空間用于存放源文件執(zhí)行過程中產(chǎn)生的名字

3、在當(dāng)前執(zhí)行文件所在的名稱空間中得到一個名字foo,該名字指向新創(chuàng)建的模塊名稱空間,若要引用模塊名稱空間中的名字,需要加上該前綴,如下

import foo #導(dǎo)入模塊foo
a=foo.x #引用模塊foo中變量x的值賦值給當(dāng)前名稱空間中的名字a
foo.get() #調(diào)用模塊foo的get函數(shù)
foo.change() #調(diào)用模塊foo中的change函數(shù)
obj=foo.Foo() #使用模塊foo的類Foo來實(shí)例化,進(jìn)一步可以執(zhí)行obj.func()

加上foo.作為前綴就相當(dāng)于指名道姓地說明要引用foo名稱空間中的名字,所以肯定不會與當(dāng)前執(zhí)行文件所在名稱空間中的名字相沖突,并且若當(dāng)前執(zhí)行文件的名稱空間中存在x,執(zhí)行foo.get()或foo.change()操作的都是源文件中的全局變量x。

插圖:惡搞圖02
08-01 模塊

需要強(qiáng)調(diào)一點(diǎn)是,第一次導(dǎo)入模塊已經(jīng)將其加載到內(nèi)存空間了,之后的重復(fù)導(dǎo)入會直接引用內(nèi)存中已存在的模塊,不會重復(fù)執(zhí)行文件,通過import sys,打印sys.modules的值可以看到內(nèi)存中已經(jīng)加載的模塊名。

提示:

#1、在Python中模塊也屬于第一類對象,可以進(jìn)行賦值、以數(shù)據(jù)形式傳遞以及作為容器類型的元素等操作。
#2、模塊名應(yīng)該遵循小寫形式,標(biāo)準(zhǔn)庫從python2過渡到python3做出了很多這類調(diào)整,比如ConfigParser、Queue、SocketServer全更新為純小寫形式。

插圖:惡搞圖03

08-01 模塊

用import語句導(dǎo)入多個模塊,可以寫多行import語句

import module1
import module2
    ...
import moduleN

還可以在一行導(dǎo)入,用逗號分隔開不同的模塊

import module1,module2,...,moduleN

但其實(shí)第一種形式更為規(guī)范,可讀性更強(qiáng),推薦使用,而且我們導(dǎo)入的模塊中可能包含有python內(nèi)置的模塊、第三方的模塊、自定義的模塊,為了便于明顯地區(qū)分它們,我們通常在文件的開頭導(dǎo)入模塊,并且分類導(dǎo)入,一類模塊的導(dǎo)入與另外一類的導(dǎo)入用空行隔開,不同類別的導(dǎo)入順序如下:

#1. python內(nèi)置模塊
#2. 第三方模塊
#3. 程序員自定義模塊

? 當(dāng)然,我們也可以在函數(shù)內(nèi)導(dǎo)入模塊,對比在文件開頭導(dǎo)入模塊屬于全局作用域,在函數(shù)內(nèi)導(dǎo)入的模塊則屬于局部的作用域。

插圖:惡搞圖04
08-01 模塊

2.2 from-import 語句

from...import...與import語句基本一致,唯一不同的是:使用import foo導(dǎo)入模塊后,引用模塊中的名字都需要加上foo.作為前綴,而使用from foo import x,get,change,Foo則可以在當(dāng)前執(zhí)行文件中直接引用模塊foo中的名字,如下

from foo import x,get,change #將模塊foo中的x和get導(dǎo)入到當(dāng)前名稱空間
a=x #直接使用模塊foo中的x賦值給a
get() #直接執(zhí)行foo中的get函數(shù)
change() #即便是當(dāng)前有重名的x,修改的仍然是源文件中的x

無需加前綴的好處是使得我們的代碼更加簡潔,壞處則是容易與當(dāng)前名稱空間中的名字沖突,如果當(dāng)前名稱空間存在相同的名字,則后定義的名字會覆蓋之前定義的名字。

插圖:惡搞圖05
08-01 模塊

另外from語句支持from foo import 語法,代表將foo中所有的名字都導(dǎo)入到當(dāng)前位置

from foo import * #把foo中所有的名字都導(dǎo)入到當(dāng)前執(zhí)行文件的名稱空間中,在當(dāng)前位置直接可以使用這些名字

a=x
get()
change()
obj=Foo()

如果我們需要引用模塊中的名字過多的話,可以采用上述的導(dǎo)入形式來達(dá)到節(jié)省代碼量的效果,但是需要強(qiáng)調(diào)的一點(diǎn)是:只能在模塊最頂層使用的方式導(dǎo)入,在函數(shù)內(nèi)則非法,并且的方式會帶來一種副作用,即我們無法搞清楚究竟從源文件中導(dǎo)入了哪些名字到當(dāng)前位置,這極有可能與當(dāng)前位置的名字產(chǎn)生沖突。模塊的編寫者可以在自己的文件中定義__all__變量用來控制*代表的意思

#foo.py
__all__=['x','get'] #該列表中所有的元素必須是字符串類型,每個元素對應(yīng)foo.py中的一個名字
x=1
def get():
    print(x)
def change():
    global x
    x=0
class Foo:
    def func(self):
       print('from the func')

這樣我們在另外一個文件中使用*導(dǎo)入時,就只能導(dǎo)入__all__定義的名字了

from foo import * #此時的*只代表x和get

x #可用
get() #可用
change() #不可用
Foo() #不可用

插圖:惡搞圖06
08-01 模塊

2.3 其他導(dǎo)入語法(as)

我們還可以在當(dāng)前位置為導(dǎo)入的模塊起一個別名

import foo as f #為導(dǎo)入的模塊foo在當(dāng)前位置起別名f,以后再使用時就用這個別名f
f.x
f.get()

還可以為導(dǎo)入的一個名字起別名

from foo import get as get_x
get_x()

通常在被導(dǎo)入的名字過長時采用起別名的方式來精簡代碼,另外為被導(dǎo)入的名字起別名可以很好地避免與當(dāng)前名字發(fā)生沖突,還有很重要的一點(diǎn)就是:可以保持調(diào)用方式的一致性,例如我們有兩個模塊json和pickle同時實(shí)現(xiàn)了load方法,作用是從一個打開的文件中解析出結(jié)構(gòu)化的數(shù)據(jù),但解析的格式不同,可以用下述代碼有選擇性地加載不同的模塊

if data_format == 'json':
    import json as serialize #如果數(shù)據(jù)格式是json,那么導(dǎo)入json模塊并命名為serialize
elif data_format == 'pickle':
    import pickle as serialize #如果數(shù)據(jù)格式是pickle,那么導(dǎo)入pickle模塊并命名為serialize

data=serialize.load(fn) #最終調(diào)用的方式是一致的

插圖:惡搞圖08
08-01 模塊

2.4 循環(huán)導(dǎo)入問題

08-01 模塊

循環(huán)導(dǎo)入問題指的是在一個模塊加載/導(dǎo)入的過程中導(dǎo)入另外一個模塊,而在另外一個模塊中又返回來導(dǎo)入第一個模塊中的名字,由于第一個模塊尚未加載完畢,所以引用失敗、拋出異常,究其根源就是在python中,同一個模塊只會在第一次導(dǎo)入時執(zhí)行其內(nèi)部代碼,再次導(dǎo)入該模塊時,即便是該模塊尚未完全加載完畢也不會去重復(fù)執(zhí)行內(nèi)部代碼

我們以下述文件為例,來詳細(xì)分析循環(huán)/嵌套導(dǎo)入出現(xiàn)異常的原因以及解決的方案

m1.py

print('正在導(dǎo)入m1')
from m2 import y

x='m1'

m2.py

print('正在導(dǎo)入m2')
from m1 import x

y='m2'

run.py

import m1

測試一

#1、執(zhí)行run.py會拋出異常
正在導(dǎo)入m1
正在導(dǎo)入m2
Traceback (most recent call last):
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa練習(xí)目錄/aa.py", line 1, in <module>
    import m1
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa練習(xí)目錄/m1.py", line 2, in <module>
    from m2 import y
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa練習(xí)目錄/m2.py", line 2, in <module>
    from m1 import x
ImportError: cannot import name 'x'

#2、分析
先執(zhí)行run.py--->執(zhí)行import m1,開始導(dǎo)入m1并運(yùn)行其內(nèi)部代碼--->打印內(nèi)容"正在導(dǎo)入m1"
--->執(zhí)行from m2 import y 開始導(dǎo)入m2并運(yùn)行其內(nèi)部代碼--->打印內(nèi)容“正在導(dǎo)入m2”--->執(zhí)行from m1 import x,由于m1已經(jīng)被導(dǎo)入過了,所以不會重新導(dǎo)入,所以直接去m1中拿x,然而x此時并沒有存在于m1中,所以報(bào)錯

插圖:惡搞圖09
08-01 模塊

測試二

#1、執(zhí)行文件不等于導(dǎo)入文件,比如執(zhí)行m1.py不等于導(dǎo)入了m1
直接執(zhí)行m1.py拋出異常
正在導(dǎo)入m1
正在導(dǎo)入m2
正在導(dǎo)入m1
Traceback (most recent call last):
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa練習(xí)目錄/m1.py", line 2, in <module>
    from m2 import y
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa練習(xí)目錄/m2.py", line 2, in <module>
    from m1 import x
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa練習(xí)目錄/m1.py", line 2, in <module>
    from m2 import y
ImportError: cannot import name 'y'

#2、分析
執(zhí)行m1.py,打印“正在導(dǎo)入m1”,執(zhí)行from m2 import y ,導(dǎo)入m2進(jìn)而執(zhí)行m2.py內(nèi)部代碼--->打印"正在導(dǎo)入m2",執(zhí)行from m1 import x,此時m1是第一次被導(dǎo)入,執(zhí)行m1.py并不等于導(dǎo)入了m1,于是開始導(dǎo)入m1并執(zhí)行其內(nèi)部代碼--->打印"正在導(dǎo)入m1",執(zhí)行from m1 import y,由于m1已經(jīng)被導(dǎo)入過了,所以無需繼續(xù)導(dǎo)入而直接問m2要y,然而y此時并沒有存在于m2中所以報(bào)錯

插圖:惡搞圖10
08-01 模塊

解決方案

# 方案一:導(dǎo)入語句放到最后,保證在導(dǎo)入時,所有名字都已經(jīng)加載過
# 文件:m1.py
print('正在導(dǎo)入m1')

x='m1'

from m2 import y

# 文件:m2.py
print('正在導(dǎo)入m2')
y='m2'

from m1 import x

# 文件:run.py內(nèi)容如下,執(zhí)行該文件,可以正常使用
import m1
print(m1.x)
print(m1.y)

# 方案二:導(dǎo)入語句放到函數(shù)中,只有在調(diào)用函數(shù)時才會執(zhí)行其內(nèi)部代碼
# 文件:m1.py
print('正在導(dǎo)入m1')

def f1():
    from m2 import y
    print(x,y)

x = 'm1'

# 文件:m2.py
print('正在導(dǎo)入m2')

def f2():
    from m1 import x
    print(x,y)

y = 'm2'

# 文件:run.py內(nèi)容如下,執(zhí)行該文件,可以正常使用
import m1

m1.f1()

注意:循環(huán)導(dǎo)入問題大多數(shù)情況是因?yàn)槌绦蛟O(shè)計(jì)失誤導(dǎo)致,上述解決方案也只是在爛設(shè)計(jì)之上的無奈之舉,在我們的程序中應(yīng)該盡量避免出現(xiàn)循環(huán)/嵌套導(dǎo)入,如果多個模塊確實(shí)都需要共享某些數(shù)據(jù),可以將共享的數(shù)據(jù)集中存放到某一個地方,然后進(jìn)行導(dǎo)入

插圖:惡搞圖11
08-01 模塊

2.5 搜索模塊的路徑與優(yōu)先級

模塊其實(shí)分為四個通用類別,分別是:

1、使用純Python代碼編寫的py文件

2、包含一系列模塊的包

3、使用C編寫并鏈接到Python解釋器中的內(nèi)置模塊

4、使用C或C++編譯的擴(kuò)展模塊

在導(dǎo)入一個模塊時,如果該模塊已加載到內(nèi)存中,則直接引用,否則會優(yōu)先查找內(nèi)置模塊,然后按照從左到右的順序依次檢索sys.path中定義的路徑,直到找模塊對應(yīng)的文件為止,否則拋出異常。sys.path也被稱為模塊的搜索路徑,它是一個列表類型

>>> sys.path
['',
'/Library/Frameworks/Python.framework/Versions/3.5/lib/python35.zip',
'/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5',
...,
'/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages'

列表中的每個元素其實(shí)都可以當(dāng)作一個目錄來看:在列表中會發(fā)現(xiàn)有.zip或.egg結(jié)尾的文件,二者是不同形式的壓縮文件,事實(shí)上Python確實(shí)支持從一個壓縮文件中導(dǎo)入模塊,我們也只需要把它們都當(dāng)成目錄去看即可。

插圖:惡搞圖12

08-01 模塊

sys.path中的第一個路徑通常為空,代表執(zhí)行文件所在的路徑,所以在被導(dǎo)入模塊與執(zhí)行文件在同一目錄下時肯定是可以正常導(dǎo)入的,而針對被導(dǎo)入的模塊與執(zhí)行文件在不同路徑下的情況,為了確保模塊對應(yīng)的源文件仍可以被找到,需要將源文件foo.py所在的路徑添加到sys.path中,假設(shè)foo.py所在的路徑為/pythoner/projects/

import sys
sys.path.append(r'/pythoner/projects/') #也可以使用sys.path.insert(……)

import foo #無論foo.py在何處,我們都可以導(dǎo)入它了

2.6 區(qū)分py文件的兩種用途

一個Python文件有兩種用途,一種被當(dāng)主程序/腳本執(zhí)行,另一種被當(dāng)模塊導(dǎo)入,為了區(qū)別同一個文件的不同用途,每個py文件都內(nèi)置了__name__變量,該變量在py文件被當(dāng)做腳本執(zhí)行時賦值為“__main__”,在py文件被當(dāng)做模塊導(dǎo)入時賦值為模塊名

插圖:惡搞圖14
08-01 模塊

作為模塊foo.py的開發(fā)者,可以在文件末尾基于__name__在不同應(yīng)用場景下值的不同來控制文件執(zhí)行不同的邏輯

#foo.py
...
if __name__ == '__main__':
    foo.py被當(dāng)做腳本執(zhí)行時運(yùn)行的代碼
else:
    foo.py被當(dāng)做模塊導(dǎo)入時運(yùn)行的代碼

通常我們會在if的子代碼塊中編寫針對模塊功能的測試代碼,這樣foo.py在被當(dāng)做腳本運(yùn)行時,就會執(zhí)行測試代碼,而被當(dāng)做模塊導(dǎo)入時則不用執(zhí)行測試代碼。

2.7 編寫一個規(guī)范的模塊

08-01 模塊

我們在編寫py文件時,需要時刻提醒自己,該文件既是給自己用的,也有可能會被其他人使用,因而代碼的可讀性與易維護(hù)性顯得十分重要,為此我們在編寫一個模塊時最好按照統(tǒng)一的規(guī)范去編寫,如下

#!/usr/bin/env python #通常只在類unix環(huán)境有效,作用是可以使用腳本名來執(zhí)行,而無需直接調(diào)用解釋器。

"The module is used to..." #模塊的文檔描述

import sys #導(dǎo)入模塊

x=1 #定義全局變量,如果非必須,則最好使用局部變量,這樣可以提高代碼的易維護(hù)性,并且可以節(jié)省內(nèi)存提高性能

class Foo: #定義類,并寫好類的注釋
    'Class Foo is used to...'
    pass

def test(): #定義函數(shù),并寫好函數(shù)的注釋
    'Function test is used to…'
    pass

if __name__ == '__main__': #主程序
    test() #在被當(dāng)做腳本執(zhí)行時,執(zhí)行此處的代碼

插圖:惡搞圖13
08-01 模塊

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)cdcxhl.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點(diǎn)與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。

網(wǎng)站題目:08-01模塊-創(chuàng)新互聯(lián)
當(dāng)前網(wǎng)址:http://bm7419.com/article20/dicdjo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供全網(wǎng)營銷推廣、手機(jī)網(wǎng)站建設(shè)、動態(tài)網(wǎng)站、App開發(fā)、小程序開發(fā)、網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都app開發(fā)公司