Python 惰性導(dǎo)入的項目實踐
不用再等待慢吞吞的模塊加載,也不用繼續(xù)依賴臨時性的繞路寫法:Python 3.15 新特性 —— lazy imports 機(jī)制已經(jīng)給出了更直接的解法。
我們知道,在 Python 中導(dǎo)入模塊時,解釋器必須先把這個模塊的代碼完整求值,程序才能繼續(xù)往下執(zhí)行。
對大多數(shù)模塊來說,聽起來沒什么問題。
但如果某個模塊的啟動過程很長、很重,那么程序就會在導(dǎo)入它的那一刻被拖慢。
以前,Python 開發(fā)者通常會通過調(diào)整導(dǎo)入位置來繞開這個問題,讓導(dǎo)入只在真正需要時才發(fā)生。
最常見的做法就是把 import 放進(jìn)函數(shù)體里,而不是寫在模塊頂層。
不過,這終究是一種不太優(yōu)雅的權(quán)宜之計,也會讓程序流程變得更難梳理。
Python 3.15 引入了一個新特性:lazy imports。
它為加載緩慢的模塊提供了一種更高層次、也更正式的解決方案。
當(dāng)一次導(dǎo)入被聲明為“惰性”時,它不會在第一次導(dǎo)入時立刻求值,而是等到第一次真正被使用時才求值。
這樣一來,慢速導(dǎo)入的成本就可以被推遲到真正需要那段代碼的時候再支付。
而且,雖然惰性導(dǎo)入帶來了新語法,你依然可以提前讓現(xiàn)有代碼適配它,而不必大面積改寫既有的導(dǎo)入語句。
立即與惰性
要理解惰性導(dǎo)入解決了什么問題,最好的方式還是先看一個例子。
開發(fā)者應(yīng)該經(jīng)常這樣寫代碼,假設(shè)同一個目錄下有這樣兩個文件:
# main.py
print("Program starting")
from other import some_fn
print("Other module imported")
some_fn()
print("Program ended")
# other.py
print("Other module evaluation started")
from time import sleep
sleep(2)
# ^ This simulates a slow-loading module
print("Other module evaluation ended")
def some_fn():
print("some_fn run")如果運行 main.py,輸出大概會是這樣:
Program starting Other module evaluation started [two-second delay] Other module evaluation ended Other module imported some_fn run Program ended
僅僅因為導(dǎo)入了 other,程序就幾乎被卡住了。
甚至在我們真正調(diào)用導(dǎo)入進(jìn)來的函數(shù)之前,更別說繼續(xù)執(zhí)行后續(xù)邏輯之前,這段等待就已經(jīng)發(fā)生了。
現(xiàn)在把 main.py 改成使用惰性導(dǎo)入,再看看會發(fā)生什么。
這只適用于 Python 3.15 或更高版本。
print("Program starting")
lazy from other import some_fn
print("Other module imported")
some_fn()
print("Program ended")
這時再運行程序,行為就會發(fā)生變化:
Program starting Other module imported Other module evaluation started [two-second delay] Other module evaluation ended some_fn run Program ended
現(xiàn)在,導(dǎo)入本身已經(jīng)不會造成任何延遲。
真正的等待,只有在我們調(diào)用那個導(dǎo)入進(jìn)來的函數(shù)時才會出現(xiàn)。
那底層到底做了什么?
當(dāng) Python 識別到惰性導(dǎo)入時,通常是因為導(dǎo)入語句前面出現(xiàn)了 lazy 關(guān)鍵字,就像上面的例子那樣,它不會走平常的導(dǎo)入流程。
取而代之的是,它會為被導(dǎo)入的模塊創(chuàng)建一個“代理對象”,也就是一個占位用的替身。
這個代理會一直待命,直到程序真的開始對這個模塊做某些操作。
到了那一刻,真正的導(dǎo)入才會觸發(fā),模塊也才會被求值。
如果你想把某一行導(dǎo)入聲明成惰性導(dǎo)入,那么 lazy 必須始終是該行的第一個詞:
# lazily imports foo lazy import foo # lazily imports bar from foo lazy from foo import bar # same with the use of "as": lazy import foo as foo1 lazy from foo import bar as bar1
用在哪里
惰性導(dǎo)入最常見的用途,就是替代那些為了避免啟動階段高成本導(dǎo)入而寫出來的傳統(tǒng)變通方案。
正如前面提到的,把導(dǎo)入寫在函數(shù)內(nèi)部而不是模塊頂層,的確能讓導(dǎo)入只在函數(shù)執(zhí)行時才發(fā)生。
但這樣做也帶來了另一個問題:這個導(dǎo)入只存在于函數(shù)作用域內(nèi)。
因此,除非你再疊加另一層繞路方案,否則模塊里的其他代碼都沒法直接使用它。
而使用惰性導(dǎo)入后,你依然可以把導(dǎo)入保留在模塊頂層,就像平常那樣寫。
你唯一需要做的,只是在代碼前面加上 lazy 關(guān)鍵字。
自動啟用惰性導(dǎo)入
你還可以在現(xiàn)有代碼庫里自動啟用惰性導(dǎo)入,而不必逐條改寫 import 語句。
Python 3.15 為 sys 模塊新增了一些能力,用來控制惰性導(dǎo)入的行為。
例如,你可以通過編程方式聲明:從程序執(zhí)行的某個時間點開始,之后所有導(dǎo)入都采用惰性方式處理。
import sys
sys.set_lazy_imports("all")
如果給 sys.set_lazy_imports() 傳入 "all",那么從那一刻起,程序中的每一次導(dǎo)入都會變成惰性導(dǎo)入,不管它是否顯式寫了 lazy 關(guān)鍵字。
如果(給該函數(shù))傳入 "normal",則只有顯式聲明的才會惰性導(dǎo)入
如果傳入 "none",則徹底關(guān)閉惰性導(dǎo)入。
以編程方式控制惰性導(dǎo)入
你也可以在運行時接管惰性導(dǎo)入的行為。
這樣一來,你就能夠進(jìn)一步控制到底哪些模塊會被惰性導(dǎo)入。
import sys
def mod_filter(importing, imported, fromlist):
return imported == "module"
sys.set_lazy_imports_filter(mod_filter)sys.set_lazy_imports_filter() 允許你傳入一個函數(shù),這個函數(shù)會接收三個參數(shù):
- 發(fā)起導(dǎo)入的模塊
- 被導(dǎo)入的模塊
- 被導(dǎo)入名稱組成的列表
借助這個函數(shù),你可以寫出自己的判斷邏輯:返回 True 時,允許某次導(dǎo)入按惰性方式進(jìn)行;返回 False 時,則強(qiáng)制它按普通方式立即導(dǎo)入。
這樣,你既可以在測試?yán)餅槎栊詫?dǎo)入維護(hù)允許列表和阻止列表,也可以把這套規(guī)則直接納入程序自身的運行機(jī)制。
使用惰性導(dǎo)入的兩種方式
Python 一直以來都很重視讓新特性能平滑進(jìn)入已有代碼庫。
惰性導(dǎo)入同樣可以按這種方式接入。
你可以在程序啟動時先檢查這個能力是否存在,然后再通過 sys.set_lazy_imports() 自動把惰性導(dǎo)入應(yīng)用到整個項目中。
最直接的辦法,是先判斷 Python 版本號:
import sys
if sys.version_info >= (3, 15):
... # set up lazy imports
或者,也可以直接檢查 sys 中是否提供了這些惰性導(dǎo)入控制接口:
import sys
if hasattr(sys, "set_lazy_imports"):
... # set up lazy imports
到此這篇關(guān)于Python 惰性導(dǎo)入的項目實踐的文章就介紹到這了,更多相關(guān)Python 惰性導(dǎo)入內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何利用Python動態(tài)模擬太陽系運轉(zhuǎn)
這篇文章主要給大家介紹了關(guān)于如何利用Python動態(tài)模擬太陽系運轉(zhuǎn)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
python jieba分詞并統(tǒng)計詞頻后輸出結(jié)果到Excel和txt文檔方法
本篇文章主要介紹了python jieba分詞并統(tǒng)計詞頻后輸出結(jié)果到Excel和txt文檔方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-02-02
Python 結(jié)巴分詞實現(xiàn)關(guān)鍵詞抽取分析
這篇文章主要介紹了Python 結(jié)巴分詞實現(xiàn)關(guān)鍵詞抽取分析,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10

