所謂的泛型, 就是將資料型別作為引數進行傳遞, 即在我們用的時候確定資料型別, 這是一種在面嚮物件語言中經常使用的特性
一般類使用
以SQLAlchemy舉例
比如: 我們統一寫個將資料儲存到資料庫的介面, 只有將資料庫連結
表物件
資料
傳入即可, 返回的是表物件的例項
, 為了讓IDE可以識別返回物件, 我們可以使用泛型
這裡需要用到:
-
typing
的TypeVar
和Type
TypeVar
是型別變數, 主要用於泛型型別與泛型函式定義的引數, 第一個引數是名稱,bound
引數用於規定該型別為bound
值的子類
Type[C]
的形式為協變數, 表明C
的所有子類都應 使用與C
相同的 構造器簽名 及 類方法簽名, 假如我們需要返回泛型型別的話, 需要用到更多使用方法, 見: typing使用
-
使用了
pydantic
規範要建立資料的型別關於
pydantic
的一般使用, 見: Pydantic使用
from typing import TypeVar, Type
from sqlalchemy.orm import Session
from pydantic import BaseModel
from orm.models import Category
from orm.schemas import WriteCategoryModel
# 定義型別
ModelT = TypeVar("ModelT")
DataT = TypeVar("DataT", bound=BaseModel) # bound表明該類為BaseModel的子類
"""
為節省空間, 表結構和模型不展示
"""
def create_category(session: Session, data: WriteCategoryModel) -> Type[Category]:
cate_obj = save_one_to_db(session=session, model_class=Category, data=data)
return cate_obj
def save_one_to_db(session: Session, model_class: ModelT, data: DataT) -> ModelT:
"""
儲存一條到資料庫
:param session: SQLAlchemy Session
:param model_class: sqlalchemy模型類
:param data: pydantic模型物件
:return: 對應sqlalchemy模型類的物件
"""
try:
obj = model_class(**data.dict())
session.add(obj)
session.commit()
# 手動將 資料 重新整理到資料庫
session.refresh(obj)
return obj
except Exception as e:
# 別忘記發生錯誤時回滾
session.rollback()
raise e
pydantic使用
在使用pydantic時, 亦可以使用泛型
比如: 在FastAPI
中返回統一返回格式為:
{
"status": true,
"msg": "success",
"data": ...
}
我們的data
可能是列表或物件, 而且對應的pydantic
模型也不一樣, 這時我們就可以使用泛型了
程式碼:
from typing import List, Optional, Generic, TypeVar
from fastapi import APIRouter, status, HTTPException
from pydantic import BaseModel
from pydantic.generics import GenericModel
router = APIRouter(prefix="/test", tags=["測試泛型"])
# 為了方便, 在這裡定義pydantic模型
DataT = TypeVar("DataT")
class GenericResponse(GenericModel, Generic[DataT]):
"""
通用返回資料
"""
status: bool
msg: str
data: Optional[DataT] = None # 可能連data都沒有
# 設定response_model_exclude_unset=True即可
class BookModel(BaseModel):
id: int
name: str
# 偽資料
fake_book_data = [
{"id": 1, "name": "book1"},
{"id": 2, "name": "book2"},
{"id": 3, "name": "book3"},
{"id": 4, "name": "book4"},
{"id": 5, "name": "book5"},
]
@router.get("/books", response_model=GenericResponse[List[BookModel]])
def get_books():
return {
"status": True,
"msg": "獲取成功",
"data": fake_book_data
}
@router.get("/books/{bid}", response_model=GenericResponse[BookModel])
def retrieve_book(bid: int):
for item in fake_book_data:
if item.get("id") == bid:
return {
"status": True,
"msg": "獲取成功",
"data": item
}
# 不存在
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="該書不存在")
訪問/docs
頁面, 成功通過測試