1. 程式人生 > >Learning-Python【16】:模塊的導入使用

Learning-Python【16】:模塊的導入使用

共享 pre 模塊導入 符號 名稱空間 charm 理解 訪問 重新

一、什麽是模塊

模塊就是一系列功能的集合體,一個模塊就是一個包含了Python定義和聲明的文件,文件名就是模塊名字加上.py的後綴。

模塊有三種來源:

  1、內置的模塊

  2、第三方的模塊

  3、自定義模塊

模塊的四種通用類別:

  1、使用Python編寫的代碼(.py文件)

  2、已被編譯為共享庫或DLL的C或C++擴展

  3、把一系列模塊組織到一起的文件夾(註:文件夾下有一個__init__.py文件,該文件夾稱之為包)

  4、使用C編寫並鏈接到Python解釋器的內置模塊

二、為何要用模塊

  1、使用內置的或者第三方模塊的好處是:拿來就可以使用,可以極大提升開發效率

  2、使用自定義模塊的好處是:可以減少代碼冗余(抽取我們自己程序中要公用的一些功能定義成模塊,然後程序的各部分組件都去模塊中調用共享的功能)

三、模塊的使用

前提:一定要區分開誰是執行文件,誰是被導入模塊

1、import導入模塊

在Pycharm中右鍵新建一個文件,文件名是spam.py,模塊名是spam,再新建一個文件叫run.py,在run.py中

import spam

首次導入一個模塊,會發生以下事情:

  1)會產生一個模塊的名稱空間

  2)執行文件spam.py,將執行過程中產生的名字都放到模塊的名稱空間中

  3)在當前執行文件的名稱空間中拿到一個模塊名,該名字指向模塊的名稱空間

之後的導入,都是直接引用第一次導入的成果,不會重新執行文件

在spam.py文件中添加代碼

money = 1000

def read1():
    print(spam模塊:,money)

def read2():
    print(spam模塊)
    read1()

def change():
    global money
    money = 0

在執行文件中訪問模塊名稱空間中名字的語法:模塊名.名字,即在run.py中

import spam

print(spam.money)   # 1000
print(spam.read1)   #
<function read1 at 0x000001CE16209B70> print(spam.read2) # <function read2 at 0x000001CE16209A60> print(spam.change) # <function change at 0x000001CE16209C80>
spam.read1()    # spam模塊: 1000

def read1():
    print(run.py --> read1)

read1()         # run.py --> read1

spam.read2()    # spam模塊
                # spam模塊: 1000
money = 9999
spam.change()
print(spam.money)   # 0
print(money)        # 9999

總結 import 導入模塊:在使用時必須加上模塊名作為前綴

優點:指名道姓的向某一個名稱空間拿取名字,不會與當前名稱空間中的名字沖突

缺點:但凡應用模塊中的名字都需要加前綴,不夠簡潔

還可以一行導入多個模塊,但不推薦使用

import spam, os, time

可以為模塊起別名(註意:模塊名應全為小寫)

import spam as sm

print(sm.money)
print(sm.read1)

2、from...import...導入模塊

首次導入模塊發生的三件事:

  1)創建一個模塊的名稱空間

  2)執行文件spam.py,將執行過程中產生的名字都放到模塊的名稱空間中

  3)在當前執行文件中直接拿到一個名字,該名字就是執行模塊中相對應的名字

刪除上面的兩個文件,重新建一個spam.py,添加代碼

money = 1000

def read1():
    print(spam模塊:,money)

def read2():
    print(spam模塊)
    read1()

def change():
    global money
    money = 0

再新建一個run.py,在run.py中

from spam import money

print(money)  # 1000
from spam import money

money = 200  
print(money)  # 與當前名稱空間中的名字沖突, 結果為200

總結from...import...導入模塊

優點:使用時,無需再加前綴,更簡潔

缺點::容易與當前名稱空間中的名字沖突

現在將run.py裏的代碼改成

from spam import money, read1

money = 200

# 這裏執行read1裏面的money還是1000,因為在定義階段就已經固定死了,與調用位置無關
read1()         # spam模塊: 1000

print(money)    # 200

還可以導入全部模塊,但不推薦使用

# 星號代表從被導入模塊中拿到所有名字
from spam import *

有一個 __all__ 的功能,將指定的模塊名以字符串的形式存放,如果再使用星號導入模塊,這時導入的就是 __all__裏面的內容,而不是導入全部的模塊,例如:

在spam.py中定義 __all__

__all__ = [money, read1]

money = 1000

def read1():
    print(spam模塊:,money)

def read2():
    print(spam模塊)
    read1()

def change():
    global money
    money = 0

在run.py中用星號導入

from spam import *

read1()     # spam模塊: 1000
read2()     # 報錯, 因為 __all__沒有包含read2模塊

可以為模塊起別名

from spam import read1 as r1

r1()

四、Python文件的兩種執行方式

1、直接運行

2、作為模塊導入

# 在m1.py中
def f1():
    print(f1)

print(__name__)    # 執行後是 __main__


# 在run.py中
import m1    # 執行後是 m1

即:當做腳本運行,__name__ 等於‘__main__‘,當做模塊導入,__name__等於模塊名

所以這個條件是:if __name__ == ‘__main__‘: 用來控制.py文件在不同的應用場景下執行不同的邏輯

# 在m1.py中
def f1():
    print(f1)

if __name__ == __main__:
    f1()


# 在run.py中
import m1

五、模塊的搜索路徑

1、模塊搜索路徑的優先級

  1)內存中已經加載過的

  2)內置模塊

  3)sys.path(第一個值是當前執行文件所在的文件夾)

模塊搜索路徑優先從內存中已加載過的開始查找,例如我新建一個m1.py和一個run.py,在run.py中導入模塊m1

# m1.py
def f1():
    print(from f1)

# run.py
import time
import m1
time.sleep(10)    # 程序睡眠10秒的過程中刪除m1.py
import m1
m1.f1()        # 這裏還可以執行

刪除m1.py文件後再導入執行不會報錯,因為在刪除的過程中程序沒有結束,再次導入是優先從內存中查找,找到了直接運行,但如果再次運行就會報錯,因為內存已經回收,找不到m1模塊了

註意:不應該將自己的模塊名命名成與內置模塊或第三方模塊的名字相同

2、添加sys.path

新建m1.py和run.py,將m1.py放在dir1文件夾下,讓dir1文件夾和run.py在同一級目錄

技術分享圖片

# m1.py
def f1():
    print(from f1)
f1()
# run.py import m1 # 這時候是找不到的,但是如果添加sys.path,將run.py改成如下

# run.py import sys sys.path.append(E://Test//dir1) # 將m1所在的文件夾路徑添加到sys.path import m1

3、from...import...

還是上面的情況,可以在run.py裏面通過from...import...導入

技術分享圖片

from dir1 import m1

這是在sys.path中找到的,但是我沒有添加sys.path,為什麽也能找到呢?

因為sys.path的第一個值是當前執行文件所在的文件夾,也就是Test文件夾(我的run.py和dir1的上一層是Test文件夾),所以說是以當前執行文件的sys.path為準,可以找到dir1,進而找到m1

基於上面的目錄,我在dir1下新建一個文件夾叫dir2,在dir2中新建一個文件叫m2.py,現在的路徑關系是:Test下面有一個run.py和dir1,dir1下有一個m1.py和dir2,dir2下有一個m2.py

技術分享圖片

m2中添加代碼

def f2():
    print(from f2)
f2()

可以通過from...import...導入

from dir1.dir2 import m2

第一種特殊情況:模塊的絕對導入

刪除上面的所有文件,新建run.py和dir1文件夾,在dir1下新建m1.py和m2.py

技術分享圖片

# m1.py
def f1():
    print(from f1)

# m2.py
def f2():
    print(from f2)

run想訪問m1,因為不在同一級目錄,所以要通過上面講到的兩種方法(添加sys.path或from...import導入)來訪問(我用後者)

# run.py
from dir1 import m1

現在我執行run.py,可以通過run訪問到m1

現在m1想訪問m2,只需要在m1中導入m2即可

# m1.py
import m2

def f1():
    print(from f1)

    m2.f2()

但是現在,我再執行run.py,能否通過run訪問到m1再訪問到m2呢?

不行!因為sys.path是以當前執行文件為準的,那麽sys.path的第一個值就是 E://Test//,在m1裏面導入m2的時候,去sys.path中無法找到m2,所以會報錯

強調:所有被導入的模塊參照的環境變量sys.path都是以執行文件為準的

那麽怎麽解決呢

# m1.py
from dir1 import m2

def f1():
    print(from f1)

    m2.f2()


# m2.py
def f2():
    print(from f2)

# run.py
from dir1 import m1

m1.f1()

註意:不要理解成sys.path是以執行文件所在的文件夾為準,因為sys.path有很多個值,而執行文件所在的文件夾僅僅只是sys.path中的一個值,這個執行文件所在的文件夾找不到後面還有很多值可以找

上面這種情況是以執行文件的sys.path作為參考點開始導入,稱之為模塊的絕對導入

優點:在執行文件與被導入的模塊中都可以使用

缺點:所有導入都是以sys.path為參考點,導入麻煩

第二種特殊情況:模塊的相對導入

參照當前所在文件的文件夾為起始開始查找,稱之為模塊的相對導入

符號:.(一個點)代表當前所在文件的文件夾,..(兩個點)代表上一級文件夾,...(三個點)代表上一級的上一級文件夾

優點:導入更加簡單

缺點:只能在被導入的模塊中使用,不能在執行文件中用

現在在Test下新建一個dir0,dir0下新建一個dir1和run.py,dir1下新建m1.py和m2.py

技術分享圖片

# run.py
from dir0.dir1 import m1    # 用的還是絕對導入

m1.f1()


# m1.py
from . import m2    # 用的是相對導入

def f1():
    print(from f1)

    m2.f2()


# m2.py
def f2():
    print(from f2)

Learning-Python【16】:模塊的導入使用