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

