如下文章來源&做者:青南(謝乾坤)html
攝影:產品經理;kingname 的第一套樂高正則表達式
你好,我是謝乾坤,前網易高級數據挖掘工程師。現任微軟最有價值專家(Python 方向),有6年 Python 開發經驗,善於解決各類業務場景下的棘手問題,進一步提高代碼質量。函數
對很多 Python 初學者來講,Python 導入其餘模塊的方式讓他們很難理解。何時用import xxx
?何時用from xxx import yyy
?何時用from xxx.yyy import zzz
?何時用from xxx import *
?學習
這篇文章,咱們來完全搞懂這個問題。編碼
以正則表達式模塊爲例,咱們常常這樣寫代碼:spa
import re target = 'abc1234xyz' re.search('(d+)', target)
但有時候,你可能會看到某些人這樣寫代碼:code
from re import search target = 'abc1234xyz' search('(d+)', target)
那麼這兩種導入方式有什麼區別呢?視頻
咱們分別使用type
函數來看看他們的類型:xml
>>> import re >>> type(re) <class 'module'> >>> from re import search >>> type(search) <class 'function'>
以下圖所示:htm
能夠看到,直接使用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.search
,re.sub
,re.S
和re.I
。後二者是常量,用於忽略換行符和大小寫。
可是,若是你使用from re import search, sub, S, I
來寫代碼,那麼代碼就會變成這樣:
import re search('c(.*?)x', flags=S) sub('[a-zA-Z0-9]', '***', target, flags=I)
看起來雖然簡潔了,可是,一旦你的代碼行數多了之後,你很容易忘記S
和I
這兩個變量是什麼東西。並且咱們本身定義的函數,也頗有可能取名爲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.py
和util.py
,他們的內容以下:
util.py
文件:
def write(): print('write 函數被調用!')
main.py
文件:
import util util.write()
運行效果以下圖所示:
如今咱們把main.py
的導入方式修改一下:
from util import write write()
依然正常運行,以下圖所示
當兩個文件在同一個文件夾下面,而且該文件夾裏面沒有__init__.py 文件時,兩種導入方式等價。
如今,咱們來建立一個文件夾microsoft
,裏面再添加一個文件parse.py
:
def read(): print('我是 microsoft 文件夾下面的 parse.py 中的 read函數')
以下圖所示:
此時咱們在 main.py
中對它進行調用:
parse.read()
運行效果以下圖所示:
咱們也能夠用另外一種方法:
from microsoft.parse import read read()
運行效果以下圖所示:
可是,你不能直接導入microsoft
,以下圖所示:
你只能導入一個模塊或者導入一個函數或者類,你不能導入一個文件夾
不管你使用的是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...
課程學習導圖以下:https://ke.sifou.com/course/1...