一日一技:Python類型標(biāo)注的高級用法
假設(shè)你正在寫后端代碼,其中一個(gè)函數(shù)的功能是傳入文章id,返回文章詳情。因?yàn)轫?xiàng)目比較大,因此在定義函數(shù)時(shí),把類型標(biāo)注加上,標(biāo)明了參數(shù)的類型和返回的類型。例如:
from typing import List
from dataclasses import dataclass
@dataclass
class ArticleDetail:
id: int
title: str
content: str
tag: List[str]
def query_article_detail(article_id: int) -> ArticleDetail:
detail = ArticleDetail(
id=article_id,
title='文章標(biāo)題',
content='文章內(nèi)容',
tag=['tag1', 'tag2']
)
return detail
def test_query_article_detail():
detail = query_article_detail(123)
print(detail.content)
現(xiàn)在,當(dāng)你拿到返回的detail變量時(shí),IDE的自動補(bǔ)全就可以正常工作了,如下圖所示。
圖片
你想讓這個(gè)函數(shù)支持批量查詢文章詳情的功能,代碼類似這樣:
def query_article_detail(article_id: int | List[int]) -> ArticleDetail | List[ArticleDetail]:
if isinstance(article_id, int):
detail = ArticleDetail(
id=article_id,
title='文章標(biāo)題',
cnotallow='文章內(nèi)容',
tag=['tag1', 'tag2']
)
return detail
else:
details = []
for _id in article_id:
detail = ArticleDetail(
id=_id,
title='文章標(biāo)題',
cnotallow='文章內(nèi)容',
tag=['tag1', 'tag2']
)
details.append(detail)
return details
如果傳入的參數(shù)是int類型的文章id,那么就返回這篇文章的詳情ArticleDetail對象。如果傳入的是文章列表,那么就返回ArticleDetail對象列表。
現(xiàn)在問題來了,由于query_article_detail函數(shù)返回的數(shù)據(jù)類型不同,如何讓IDE的自動補(bǔ)全能夠正確提示呢?例如當(dāng)我們傳入了一個(gè)文章id列表,但是卻直接讀取返回?cái)?shù)據(jù)的.content屬性,在IDE上面看不出任何問題,如下圖所示。但顯然會報(bào)錯(cuò),因?yàn)榇藭r(shí)的detail變量的值是一個(gè)列表。列表是沒有.content屬性的。
圖片
有沒有什么辦法能夠讓IDE根據(jù)query_article_detail參數(shù)的類型,提示我們對返回?cái)?shù)據(jù)的使用是否正確呢?
這個(gè)場景下,就可以使用Python的typing模塊中的@overload裝飾器,實(shí)現(xiàn)函數(shù)重載來提示。示例代碼如下:
from typing import List, overload
from dataclasses import dataclass
@dataclass
class ArticleDetail:
id: int
title: str
content: str
tag: List[str]
@overload
def query_article_detail(article_id: List[int]) -> List[ArticleDetail]:
...
@overload
def query_article_detail(article_id: int) -> ArticleDetail:
...
def query_article_detail(article_id: int | List[int]) -> ArticleDetail | List[ArticleDetail]:
if isinstance(article_id, int):
detail = ArticleDetail(
id=article_id,
title='文章標(biāo)題',
cnotallow='文章內(nèi)容',
tag=['tag1', 'tag2']
)
return detail
else:
details = []
for _id in article_id:
detail = ArticleDetail(
id=_id,
title='文章標(biāo)題',
cnotallow='文章內(nèi)容',
tag=['tag1', 'tag2']
)
details.append(detail)
return details
def test_query_article_detail():
detail = query_article_detail([123, 456, 789])
print(detail.)
在定義函數(shù)之前,先使用@overload裝飾器,裝飾兩次函數(shù)名。每一次使用不同的參數(shù):
@overload
def query_article_detail(article_id: List[int]) -> List[ArticleDetail]:
...
@overload
def query_article_detail(article_id: int) -> ArticleDetail:
...
這兩個(gè)函數(shù)都是空函數(shù),函數(shù)體用三個(gè)點(diǎn)代替。當(dāng)然你也可以使用pass。而你真正的query_article_detail放到最下面。現(xiàn)在,當(dāng)我們對detail對象使用自動補(bǔ)全時(shí),IDE就能根據(jù)參數(shù)的類型來補(bǔ)全對應(yīng)的值了。
當(dāng)傳入?yún)?shù)是單個(gè)id時(shí),如下圖所示:
圖片
當(dāng)傳入的參數(shù)是id列表時(shí),如下圖所示:
圖片
需要注意的時(shí),所有重載的函數(shù)與真正執(zhí)行的函數(shù),函數(shù)名必須全部相同,如下圖所示:
圖片
并且,真正實(shí)現(xiàn)功能的函數(shù),必須放在重載函數(shù)的下面。
使用這種方式,以后即時(shí)別的文件導(dǎo)入并使用你這個(gè)函數(shù),你也不用擔(dān)心它用錯(cuò)數(shù)據(jù)類型了。