徹底搞懂Python 中的 import 與 from import

思否程式設計發表於2020-12-24

以下文章來源&作者:青南(謝乾坤)

圖片

攝影:產品經理;kingname 的第一套樂高

你好,我是謝乾坤,前網易高階資料探勘工程師。現任微軟最有價值專家(Python 方向),有6年 Python 開發經驗,善於解決各種業務場景下的棘手問題,進一步提升程式碼質量。

對不少 Python 初學者來說,Python 匯入其他模組的方式讓他們很難理解。什麼時候用import xxx?什麼時候用from xxx import yyy?什麼時候用from xxx.yyy import zzz?什麼時候用from xxx import *

這篇文章,我們來徹底搞懂這個問題。

系統自帶的模組


以正規表示式模組為例,我們經常這樣寫程式碼:

import re
target = 'abc1234xyz'
re.search('(d+)', target)

但有時候,你可能會看到某些人這樣寫程式碼:

from re import search
target = 'abc1234xyz'
search('(d+)', target)

那麼這兩種匯入方式有什麼區別呢?

我們分別使用type函式來看看他們的型別:

>>> import re
>>> type(re)
<class 'module'>
>>> from re import search
>>> type(search)
<class 'function'>

如下圖所示:

圖片

可以看到,直接使用import re匯入的re它是一個module類,也就是模組。我們把它成為正規表示式模組。而當我們from re import search時,這個search是一個function類,我們稱呼它為search 函式

一個模組裡面可以包含多個函式。

如果在你的程式碼裡面,你已經確定只使用search函式,不會再使用正規表示式裡面的其他函式了,那麼你使用兩種方法都可以,沒什麼區別。

但是,如果你要使用正規表示式下面的多個函式,或者是一些常量,那麼用第一種方案會更加簡潔清晰。

例如:

import re
re.search('c(.*?)x', flags=re.S)
re.sub('[a-zA-Z0-9]', '***', target, flags=re.I)

在這個例子中,你分別使用了re.searchre.subre.Sre.I。後兩者是常量,用於忽略換行符和大小寫。

但是,如果你使用from re import search, sub, S, I來寫程式碼,那麼程式碼就會變成這樣:

import re
search('c(.*?)x', flags=S)
sub('[a-zA-Z0-9]', '***', target, flags=I)

看起來雖然簡潔了,但是,一旦你的程式碼行數多了以後,你很容易忘記SI這兩個變數是什麼東西。而且我們自己定義的函式,也很有可能取名為sub或者search,從而覆蓋正規表示式模組下面的這兩個同名函式。這就會導致很多難以覺察的潛在 bug。

再舉一個例子。Python 的 datetime模組,我們可以直接import datetime,此時我們匯入的是一個datetime模組,如下圖所示:

圖片

但是如果你寫為from datetime import datetime,那麼你匯入的datetime是一個type類:

圖片

因為這種方式匯入的datetime,它就是Python 中的一種型別,用於表示包含日期和時間的資料。

這兩種匯入方式匯入的datetime,雖然名字一樣,但是他們的意義完全不一樣,請大家觀察下面兩種寫法:

import datetime
now = datetime.datetime.now()
one_hour_ago = now - datetime.timedelta(hours=1)
from datetime import datetime, timedelta
now = datetime.now()
one_hour_ago = now - timedelta(hours=1)

第二種寫法看似簡單,但實則改動起來卻更為麻煩。例如我還需要增加一個變數today用於記錄今日的日期。

對於第一段程式碼,我們只需要增加一行即可:

today = datetime.date.today()

但對於第二行來說,我們需要首先修改匯入部分的程式碼:

from datetime import datetime, timedelta, date

然後才能改程式碼:today = date.today()

這樣一來你就要修改兩個地方,反倒增加了負擔。

第三方庫


在使用某些第三方庫的程式碼裡面,我們會看到類似這樣的寫法:

 from lxml.html import fromstring
 
 selector = fromstring(HTML)

但是我們還可以寫為:

from lxml import html
selector = html.fromstring(HTML)

但是,下面這種寫法會導致報錯:

import lxml
selector = lxml.html.fromstring(HTML)

那麼這裡的lxml.html又是什麼東西呢?

這種情況多常見於一些特別大型的第三方庫中,這種庫能處理多種型別的資料。例如lxml它既能處理xml的資料,又能處理html的資料,於是這種庫會劃分子模組,lxml.html模組專門負責html相關的資料。

自己來實現多種匯入方法


我們現在自己來寫程式碼,實現這多種匯入方法。

我們建立一個資料夾DocParser,在裡面分別建立兩個檔案main.pyutil.py,他們的內容如下:

util.py檔案:

def write():
    print('write 函式被呼叫!')

main.py檔案:

import util
util.write()

執行效果如下圖所示:

image.png

現在我們把main.py的匯入方式修改一下:

from util import write
write()

依然正常執行,如下圖所示
image.png

 當兩個檔案在同一個資料夾下面,並且該資料夾裡面沒有__init__.py 檔案時,兩種匯入方式等價。

現在,我們來建立一個資料夾microsoft,裡面再新增一個檔案parse.py

def read():
    print('我是 microsoft 資料夾下面的 parse.py 中的 read函式')

如下圖所示:

image.png

此時我們在 main.py中對它進行呼叫:

parse.read()

執行效果如下圖所示:

image.png

我們也可以用另一種方法:

from microsoft.parse import read
read()

執行效果如下圖所示:
image.png

但是,你不能直接匯入microsoft,如下圖所示:

image.png


你只能匯入一個模組或者匯入一個函式或者類,你不能匯入一個資料夾

無論你使用的是import xxx還是from xxx.yyy.zzz.www import qqq,你匯入進來的東西,要不就是一個模組(對應到.py 檔案的檔名),或者是某個.py 檔案中的函式名、類名、變數名。

無論是import xxx還是from xxx import yyy,你匯入進來的都不能是一個資料夾的名字。

可能有這樣一種情況,就是某個函式名與檔案的名字相同,例如:

microsoft資料夾裡面有一個microsoft.py檔案,這個檔案裡面有一個函式叫做microsoft,那麼你的程式碼可以寫為:

from microsoft import microsoft`
microsoft.microsoft()

但請注意分辨,這裡你匯入的還是模組,只不過microsoft.py檔名與它所在的資料夾名恰好相同而已。

總結


無論是使用import還是from import,第一個要求是程式碼能夠正常執行,其次,根據程式碼維護性,團隊編碼風格來確定選擇哪一種方案。

如果我們只會使用到某個模組下面的一個函式(或者常量、類)並且名字不會產生混淆,可識別性高,那麼from 模組名 import 函式名這沒有什麼問題。

如果我們會用到一個模組下面的多個函式,或者是我們將要使用的函式名、常量名、類名可能會讓人產生混淆(例如 re.S、re.I),那麼這種情況下,import 模組名然後再 模組名.xxx來呼叫會讓程式碼更加清晰,更好維護。

但無論什麼情況下,都禁止使用from xxx import *這種寫法,它會給你帶來無窮無盡的噩夢。

更多內容


Python 開發中的坑不在少數。不僅會嚴重破壞程式碼的穩定性,還會影響專案程式碼開發效率,自身的職業發展甚至是工作狀態。

其實,我們並不是不想解決問題、並不是甘於編寫所謂“漏洞百出”的程式碼。只是不知道問題出在哪裡、為什麼會出現、應該怎樣修改。

多年的業務開發,我詳盡記錄了多個真實發生的錯誤、坑點,並提煉出 42 章節的《Python 業務開發常見錯誤案例集》視訊課程。

錯誤坑點主要分為程式碼編寫、開發思想兩類。

點選連結,檢視視訊課詳情:https://ke.sifou.com/course/1...
image

課程學習導圖如下:https://ke.sifou.com/course/1...
image

相關文章