08-01模塊

一 模塊介紹

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

創(chuàng)新互聯(lián)公司是一家以網(wǎng)站建設(shè)公司、網(wǎng)頁(yè)設(shè)計(jì)、品牌設(shè)計(jì)、軟件運(yùn)維、成都網(wǎng)站推廣、小程序App開(kāi)發(fā)等移動(dòng)開(kāi)發(fā)為一體互聯(lián)網(wǎng)公司。已累計(jì)為成都OPP膠袋等眾行業(yè)中小客戶(hù)提供優(yōu)質(zhì)的互聯(lián)網(wǎng)建站和軟件開(kāi)發(fā)服務(wù)。

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

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

二 模塊的使用

2.1 import語(yǔ)句

有如下示范文件

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

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

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

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

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

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

加上foo.作為前綴就相當(dāng)于指名道姓地說(shuō)明要引用foo名稱(chēng)空間中的名字,所以肯定不會(huì)與當(dāng)前執(zhí)行文件所在名稱(chēng)空間中的名字相沖突,并且若當(dāng)前執(zhí)行文件的名稱(chēng)空間中存在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)入會(huì)直接引用內(nèi)存中已存在的模塊,不會(huì)重復(fù)執(zhí)行文件,通過(guò)import sys,打印sys.modules的值可以看到內(nèi)存中已經(jīng)加載的模塊名。

提示:

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

插圖:惡搞圖03

08-01 模塊

用import語(yǔ)句導(dǎo)入多個(gè)模塊,可以寫(xiě)多行import語(yǔ)句

import module1
import module2
    ...
import moduleN

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

import module1,module2,...,moduleN

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

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

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

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

2.2 from-import 語(yǔ)句

from...import...與import語(yǔ)句基本一致,唯一不同的是:使用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)前名稱(chēng)空間
a=x #直接使用模塊foo中的x賦值給a
get() #直接執(zhí)行foo中的get函數(shù)
change() #即便是當(dāng)前有重名的x,修改的仍然是源文件中的x

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

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

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

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

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

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

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

這樣我們?cè)诹硗庖粋€(gè)文件中使用*導(dǎo)入時(shí),就只能導(dǎo)入__all__定義的名字了

from foo import * #此時(shí)的*只代表x和get

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

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

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

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

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

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

from foo import get as get_x
get_x()

通常在被導(dǎo)入的名字過(guò)長(zhǎng)時(shí)采用起別名的方式來(lái)精簡(jiǎn)代碼,另外為被導(dǎo)入的名字起別名可以很好地避免與當(dāng)前名字發(fā)生沖突,還有很重要的一點(diǎn)就是:可以保持調(diào)用方式的一致性,例如我們有兩個(gè)模塊json和pickle同時(shí)實(shí)現(xiàn)了load方法,作用是從一個(gè)打開(kāi)的文件中解析出結(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)入問(wèn)題

08-01 模塊

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

我們以下述文件為例,來(lá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

測(cè)試一

#1、執(zhí)行run.py會(huì)拋出異常
正在導(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,開(kāi)始導(dǎo)入m1并運(yùn)行其內(nèi)部代碼--->打印內(nèi)容"正在導(dǎo)入m1"
--->執(zhí)行from m2 import y 開(kāi)始導(dǎo)入m2并運(yùn)行其內(nèi)部代碼--->打印內(nèi)容“正在導(dǎo)入m2”--->執(zhí)行from m1 import x,由于m1已經(jīng)被導(dǎo)入過(guò)了,所以不會(huì)重新導(dǎo)入,所以直接去m1中拿x,然而x此時(shí)并沒(méi)有存在于m1中,所以報(bào)錯(cuò)

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

測(cè)試二

#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,此時(shí)m1是第一次被導(dǎo)入,執(zhí)行m1.py并不等于導(dǎo)入了m1,于是開(kāi)始導(dǎo)入m1并執(zhí)行其內(nèi)部代碼--->打印"正在導(dǎo)入m1",執(zhí)行from m1 import y,由于m1已經(jīng)被導(dǎo)入過(guò)了,所以無(wú)需繼續(xù)導(dǎo)入而直接問(wèn)m2要y,然而y此時(shí)并沒(méi)有存在于m2中所以報(bào)錯(cuò)

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

解決方案

# 方案一:導(dǎo)入語(yǔ)句放到最后,保證在導(dǎo)入時(shí),所有名字都已經(jīng)加載過(guò)
# 文件: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)入語(yǔ)句放到函數(shù)中,只有在調(diào)用函數(shù)時(shí)才會(huì)執(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)入問(wèn)題大多數(shù)情況是因?yàn)槌绦蛟O(shè)計(jì)失誤導(dǎo)致,上述解決方案也只是在爛設(shè)計(jì)之上的無(wú)奈之舉,在我們的程序中應(yīng)該盡量避免出現(xiàn)循環(huán)/嵌套導(dǎo)入,如果多個(gè)模塊確實(shí)都需要共享某些數(shù)據(jù),可以將共享的數(shù)據(jù)集中存放到某一個(gè)地方,然后進(jìn)行導(dǎo)入

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

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

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

1、使用純Python代碼編寫(xiě)的py文件

2、包含一系列模塊的包

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

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

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

>>> 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'

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

插圖:惡搞圖12

08-01 模塊

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

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

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

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

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

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

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

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

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

2.7 編寫(xiě)一個(gè)規(guī)范的模塊

08-01 模塊

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

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

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

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

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

class Foo: #定義類(lèi),并寫(xiě)好類(lèi)的注釋
    'Class Foo is used to...'
    pass

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

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

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

分享標(biāo)題:08-01模塊
URL地址:http://muchs.cn/article2/gdeeic.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供商城網(wǎng)站用戶(hù)體驗(yàn)、營(yíng)銷(xiāo)型網(wǎng)站建設(shè)、標(biāo)簽優(yōu)化響應(yīng)式網(wǎng)站、ChatGPT

廣告

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

h5響應(yīng)式網(wǎng)站建設(shè)