在学习 Python 3.10 新特性时,其中有个类型别名(TypeAlias), 所举的例子是
StrCache = 'Cache[str]' # a type alias
LOG_PREFIX = 'LOG[DEBUG]' # a module constant
可写成
StrCache: TypeAlias = 'Cache[str]' # a type alias
LOG_PREFIX = 'LOG[DEBUG]' # a module constant
这让 StrCache 更像是一个类型别名,而不是一个看起来明显就是 Cache[str]
的字符串变量(实际上它确实是)。
本文不在 TypeAlias 本身,而是从 Cache[str]
能看出 Python 似乎也能支持像 Java 那样的泛型, 就像 Python 内置支持的 List[str] 或 list[str] 那样。
那么来看 Python 怎么去实现一个只能放入字符串的 Cache[str]
Cache, 而不能放入别的类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
from typing import TypeVar, Generic T = TypeVar('T') class Cache(Generic[T]): def __init__(self) -> None: self.items: dict[str, T] = {} def put(self, key: str, item: T) -> None: self.items[key] = item # Line 13 def get(self, key: str) -> T: return self.items.get(key) |
我们依然用到的是 Python 的 typing 模块,所以上面代码仍然是属于类 type hints 的范畴,对 Python 解释器不构成约束,只会影响到 mypy 这样的检查工具
使用上面的类
1 2 3 4 5 6 7 |
cache = Cache[str]() cache.put('a', 'abc') print(cache.get('a')) cache.put('b', 123) # Line 21 print(cache.get('b')) |
使用 python test.py
命令执行一切正常,输出为
abc
123
但在 IntelliJ IDEA 中,上面高亮的第五行 cache.put('b', 123)
行上会提示
Expected type 'str' (matched generic type 'T'), got 'int' instead
如果用 mypy
来检测
$ mypy tt.py
tt.py:13: error: Incompatible return value type (got "Optional[T]", expected "T")
tt.py:21: error: Argument 2 to "put" of "Cache" has incompatible type "int"; expected "str"
Found 2 errors in 1 file (checked 1 source file)
typing 模块中除了上面用到的 Generic, TypeVar 外,还有更多的类型约束,像 Mapping, Iterator, Sequence。还有更多的类似于 Java 的泛型功能
泛型子类型
1 2 3 4 5 6 7 8 9 10 |
from typing import TypeVar, Generic T = TypeVar('T') class Cache(Generic[T]): pass class TTLCache(Cache[T]): pass |
泛型函数
1 2 3 4 5 6 |
from typing import TypeVar, Sequence T = TypeVar('T') # Declare type variable def first(seq: Sequence[T]) -> T: # Generic function return seq[0] |
更多用法,在此不一一列出。写自己的实现代码使用泛型的需要其实并不大,除非做第三方的通用库会用到泛型写法,这让别人用起来更清晰。
更多功能请参考 https://mypy.readthedocs.io/en/stable/generics.html
链接:
本文链接 https://yanbin.blog/python-generic-implementation/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。