Generics in Python
泛型(Generics)
https://www.geeksforgeeks.org/generics-in-java/
一般类的定义,其引用类是要在其代码中明确指定的
但是对于一些上层公共逻辑,其期望可以应用到若干引用类中
则定义泛型类, 可以满足这种需求。
泛型类在使用的使用, 才绑定具体的引用类。
Generics means parameterized types. The idea is to allow type (Integer, String, … etc., and user-defined types) to be a parameter to methods, classes, and interfaces. Using Generics, it is possible to create classes that work with different data types. An entity such as class, interface, or method that operates on a parameterized type is a generic entity.
// Java program to show working of user defined // Generic classes // We use < > to specify Parameter type class Test<T> { // An object of type T is declared T obj; Test(T obj) { this.obj = obj; } // constructor public T getObject() { return this.obj; } } // Driver class to test above class Main { public static void main(String[] args) { // instance of Integer type Test<Integer> iObj = new Test<Integer>(15); System.out.println(iObj.getObject()); // instance of String type Test<String> sObj = new Test<String>("GeeksForGeeks"); System.out.println(sObj.getObject()); } }
Generics in Python
https://textbooks.cs.ksu.edu/cc410/iii-web/20-extras/03-python-generics/
如下,对于需要依赖的引用类, 定义类型变量 T
然后再类定义时候, 指定其继承 Generic 类,指定T
from typing import List, TypeVar, Generic T = TypeVar('T') class MyGenericList(Generic[T]): def __init__(self) -> None: self.__array: List[T] = list() def append(self, obj: T) -> None: self.__array.append(obj) def get(self, i: int) -> T: self.__array[i]
generic_list: MyGenericList[Person] = MyGenericList() person: Person = Person("Willie", 42) generic_list.append(person) person_out: Person = generic_list[0] # Properly Type Checked my_person_list.append("Test") # TYPE CHECK ERROR
typing.TypeVar
https://docs.python.org/3/library/typing.html#typing.TypeVar
在注解中, 你使用的类型一般是固定的, 是啥就写啥
但是总有一些场景要求, 变量的类型是可变的, 这就引入了 类型变量。
Type variables exist primarily for the benefit of static type checkers. They serve as the parameters for generic types as well as for generic function definitions. See
Generic
for more information on generic types. Generic functions work as follows:
T = TypeVar('T') # Can be anything S = TypeVar('S', bound=str) # Can be any subtype of str A = TypeVar('A', str, bytes) # Must be exactly str or bytes def repeat(x: T, n: int) -> Sequence[T]: """Return a list containing n references to x.""" return [x]*n def print_capitalized(x: S) -> S: """Print x capitalized, and return x.""" print(x.capitalize()) return x def concatenate(x: A, y: A) -> A: """Add two strings or bytes objects together.""" return x + y
typing.Generic
https://docs.python.org/3/library/typing.html#typing.Generic
泛型基础类
泛型类需要继承此基础类, 并定义类型变量, 这些类型变量类似函数的形参。
Abstract base class for generic types.
A generic type is typically declared by inheriting from an instantiation of this class with one or more type variables. For example, a generic mapping type might be defined as:
class Mapping(Generic[KT, VT]): def __getitem__(self, key: KT) -> VT: ... # Etc.
使用的时候, 先定义类型变量, 然后绑定Generic类,类似函数调用时候填充的实际参数。
X = TypeVar('X') Y = TypeVar('Y') def lookup_name(mapping: Mapping[X, Y], key: X, default: Y) -> Y: try: return mapping[key] except KeyError: return default
fastapi crud project application
https://github.com/tiangolo/full-stack-fastapi-postgresql/blob/490c554e23343eec0736b06e59b2108fdd057fdc/%7B%7Bcookiecutter.project_slug%7D%7D/backend/app/app/crud/base.py
from typing import Any, Dict, Generic, List, Optional, Type, TypeVar, Union from fastapi.encoders import jsonable_encoder from pydantic import BaseModel from sqlalchemy.orm import Session from app.db.base_class import Base ModelType = TypeVar("ModelType", bound=Base) CreateSchemaType = TypeVar("CreateSchemaType", bound=BaseModel) UpdateSchemaType = TypeVar("UpdateSchemaType", bound=BaseModel) class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): def __init__(self, model: Type[ModelType]): """ CRUD object with default methods to Create, Read, Update, Delete (CRUD). **Parameters** * `model`: A SQLAlchemy model class * `schema`: A Pydantic model (schema) class """ self.model = model def get(self, db: Session, id: Any) -> Optional[ModelType]: return db.query(self.model).filter(self.model.id == id).first() def get_multi( self, db: Session, *, skip: int = 0, limit: int = 100 ) -> List[ModelType]: return db.query(self.model).offset(skip).limit(limit).all() def create(self, db: Session, *, obj_in: CreateSchemaType) -> ModelType: obj_in_data = jsonable_encoder(obj_in) db_obj = self.model(**obj_in_data) # type: ignore db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def update( self, db: Session, *, db_obj: ModelType, obj_in: Union[UpdateSchemaType, Dict[str, Any]] ) -> ModelType: obj_data = jsonable_encoder(db_obj) if isinstance(obj_in, dict): update_data = obj_in else: update_data = obj_in.dict(exclude_unset=True) for field in obj_data: if field in update_data: setattr(db_obj, field, update_data[field]) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def remove(self, db: Session, *, id: int) -> ModelType: obj = db.query(self.model).get(id) db.delete(obj) db.commit() return
https://github.com/tiangolo/full-stack-fastapi-postgresql/blob/490c554e23343eec0736b06e59b2108fdd057fdc/%7B%7Bcookiecutter.project_slug%7D%7D/backend/app/app/crud/crud_item.py
from typing import List from fastapi.encoders import jsonable_encoder from sqlalchemy.orm import Session from app.crud.base import CRUDBase from app.models.item import Item from app.schemas.item import ItemCreate, ItemUpdate class CRUDItem(CRUDBase[Item, ItemCreate, ItemUpdate]): def create_with_owner( self, db: Session, *, obj_in: ItemCreate, owner_id: int ) -> Item: obj_in_data = jsonable_encoder(obj_in) db_obj = self.model(**obj_in_data, owner_id=owner_id) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def get_multi_by_owner( self, db: Session, *, owner_id: int, skip: int = 0, limit: int = 100 ) -> List[Item]: return ( db.query(self.model) .filter(Item.owner_id == owner_id) .offset(skip) .limit(limit) .all() ) item = CRUDItem(Item)