一日一技:如何實(shí)現(xiàn)高性能自動(dòng)補(bǔ)全?
我們知道,在寫Python時(shí),使用IDE的自動(dòng)補(bǔ)全功能,可以大大提高代碼的開發(fā)效率。使用類型標(biāo)注功能,可以讓IDE知道應(yīng)該怎么做自動(dòng)補(bǔ)全。
當(dāng)我們沒有類型標(biāo)注時(shí),IDE并不知道函數(shù)的某個(gè)參數(shù)是什么東西,沒有辦法做補(bǔ)全,如下圖所示。
圖片
但當(dāng)我們把類型標(biāo)注加上以后,IDE就能正常補(bǔ)全了,如下圖所示:
圖片
這樣做,需要從另一個(gè)文件中,把這個(gè)參數(shù)對(duì)應(yīng)的類導(dǎo)入到當(dāng)前文件里面,然后把類作為類型填寫到函數(shù)參數(shù)后面。咋看起來沒有什么問題,并且我,還有很多看文章的同學(xué),應(yīng)該經(jīng)常這樣寫類型標(biāo)注的代碼,從而提高代碼的開發(fā)效率。
但如果你的項(xiàng)目規(guī)模大起來以后,你就會(huì)遇到幾個(gè)比較麻煩的問題:
- 導(dǎo)入鏈過長(zhǎng):例如上面截圖中的代碼,我從model.py中導(dǎo)入了Detail這個(gè)類。如果我在model.py文件的開頭,還有from aaa import bbb,而在aaa.py文件開頭,又有from ccc import ddd;在ccc.py開頭,又有from xxx import yyy……這個(gè)導(dǎo)入鏈條就會(huì)變得很長(zhǎng)。雖然Python對(duì)模塊導(dǎo)入已經(jīng)做了緩存,多次執(zhí)行from xxx import yyy時(shí),只有第一次會(huì)生效,后面都是讀取緩存,但讀取緩存也會(huì)消耗一些時(shí)間。
- 循環(huán)依賴:一般情況下,你的代碼能夠正常運(yùn)行,那么應(yīng)該是不會(huì)存在循環(huán)依賴的。否則肯定報(bào)錯(cuò)了。但現(xiàn)在你在一個(gè)原來的依賴鏈條之外的文件中,為了做類型標(biāo)注,導(dǎo)入了一個(gè)已有的文件。此時(shí)有可能就會(huì)引入循環(huán)依賴。特別是當(dāng)代碼規(guī)模大起來以后,如果一開始沒有設(shè)計(jì)好代碼結(jié)構(gòu),稍不注意就會(huì)出現(xiàn)循環(huán)依賴。
如果你引入一個(gè)類,僅僅是為了做類型標(biāo)注,那么這個(gè)問題實(shí)際上非常好解決。在Python的typing模塊里面,有一個(gè)常量,叫做TYPE_CHECKING,它就是為了解決這個(gè)問題而設(shè)計(jì)的。在你使用python xxx.py來啟動(dòng)代碼時(shí),TYPE_CHECKING的值是False。但當(dāng)IDE的類型檢查或者M(jìn)ypy這種靜態(tài)類型檢查工具運(yùn)行時(shí),TYPE_CHECKING的值是True。
因此,我們可以使用下面這段代碼,來提高代碼的運(yùn)行效率,同時(shí)規(guī)避循環(huán)依賴的問題:
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from xxx import YYY
def parse_detail(params: 'YYY'):
...
注意,在函數(shù)參數(shù)的類型標(biāo)注里面,類YYY需要以字符串的形式寫出。如下圖所示:
圖片
使用這種方法,在寫代碼時(shí),IDE能夠正確的做自動(dòng)補(bǔ)全。在Mypy做靜態(tài)類型檢查時(shí),也能過正常通過檢查。但當(dāng)代碼實(shí)際運(yùn)行時(shí),會(huì)自動(dòng)忽略這個(gè)導(dǎo)入的類,從而避免對(duì)代碼的運(yùn)行效率造成影響。