Python泛型函数依赖运行时行为适配而非编译期类型检查,核心是鸭子类型与协议约束;TypeVar需配合bound或constraints才能发挥静态检查作用,避免Union滥用和isinstance堆砌,优先使用collections.abc抽象基类及functools.singledispatch实现清晰分发。

Python泛型函数不是靠类型声明,而是靠运行时行为适配
Python本身没有编译期泛型(如Java或Rust),所谓“泛型函数”实际是函数能接受多种类型输入并合理处理——关键不在标注,而在逻辑是否健壮。类型提示(typing.Generic、TypeVar)只用于静态检查和IDE提示,不改变运行行为。
常见误操作是过度依赖Union或写一堆isinstance分支,结果函数越来越臃肿、难以维护。真正实用的泛型函数,往往靠鸭子类型(duck typing)或协议(Protocol)来隐式约束。
用TypeVar加约束让类型提示真正起作用
单纯写T = TypeVar("T")意义有限;只有加上bound=或constraints=,mypy才能帮你捕获明显类型错误。比如要支持所有可相加类型(int、str、list),但排除dict这种不支持+的:
from typing import TypeVar, Union
<h1>错误:太宽泛,mypy无法校验 + 是否合法</h1><p>T = TypeVar("T")</p><p><span>立即学习</span>“<a href="https://pan.quark.cn/s/00968c3c2c15" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">Python免费学习笔记(深入)</a>”;</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/skill5288" title="提示词大师-python版"><img
src="https://img.php.cn/upload/skill/000/000/081/179042051830184.jpg" alt="提示词大师-python版" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/skill5288" title="提示词大师-python版">提示词大师-python版</a>
<p>图片提示词生成器?不止如此。
马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。
用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。
用得越多,它越快:缓存机制让后续对话越来越省。
RAG进化:成功案例持续入库,越跑越聪明。
输入「新手指南」查看完整功能介绍</p>
</div>
<a href="/xiazai/skill5288" title="提示词大师-python版" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div><h1>正确:用 bound 限定为支持 <strong>add</strong> 的类型</h1><p>class Addable:
def <strong>add</strong>(self, other): ...</p><p>T = TypeVar("T", bound=Addable)</p><p>def concat(a: T, b: T) -> T:
return a + b
-
bound=Addable比Union[int, str, list]更可持续——未来新增支持__add__的类(如自定义向量)自动兼容 - 注意:
Addable是空协议类,仅作类型检查用途,运行时不生效 - 若需更精确控制(如要求返回同类型),必须在函数体内手动校验
type(a) == type(b),类型系统无法保证
避免isinstance堆砌,优先用collections.abc抽象基类
当需要区分“可迭代”“映射”“字符串”等行为时,直接比对具体类型(如type(x) is list)会漏掉tuple、array.array等合法变体。应使用标准ABC:
from collections.abc import Iterable, Mapping, Sequence from typing import Any <p>def flatten(obj: Any) -> list: if isinstance(obj, (str, bytes)): # 字符串是Iterable但不该递归展开 return [obj] elif isinstance(obj, Mapping): return [item for pair in obj.items() for item in flatten(pair)] elif isinstance(obj, Iterable): return [item for sub in obj for item in flatten(sub)] else: return [obj]
-
isinstance(obj, str)必须显式写在Iterable之前,否则"abc"会被当成Iterable无限递归 -
Sequence比list更合适表示“有序可索引”,但别滥用——多数场景只需Iterable即可 - 这类函数无法用
TypeVar精确建模输入/输出类型关系,类型提示只能写Any或保守的object
运行时类型分发用functools.singledispatch而非if链
当不同类型的处理逻辑差异大(比如序列拼接 vs 字典合并 vs 数值加法),硬塞进一个函数会导致条件判断爆炸。singledispatch提供清晰的分发入口:
from functools import singledispatch
from typing import Union, Dict, List, Any
<p>@singledispatch
def merge(a, b):
raise TypeError(f"Cannot merge {type(a)} and {type(b)}")</p><p>@merge.register
def _(a: dict, b: dict) -> Dict:
return {<strong>a, </strong>b}</p><p>@merge.register
def _(a: list, b: list) -> List:
return a + b</p><p>@merge.register
def _(a: int, b: int) -> int:
return a + b
- 注册函数名用
_避免污染命名空间,且无需考虑参数名一致性(a和b在各注册函数中可不同名) - 不支持多参数分发(如同时按
a和b类型选择),此时需嵌套singledispatch或改用typing.overload+ 运行时if - 注意:装饰器只检查第一个参数类型,
merge({"x":1}, "hello")仍会进入dict分支,后续需手动校验b类型
真正难的是边界情况:比如merge([1,2], (3,4))该返回list还是tuple?类型系统不会替你做业务决策,得靠文档明确约定,或在函数开头加assert isinstance(b, type(a))强制一致。

















