Union
## 关于 Python 中的 Union你可能需要知道这些在 Python 的开发过程中经常会遇到一个变量可能具有多种类型的情况。比如从网络接口获取数据可能是字符串也可能是数字甚至是None。早期处理这种情况时要么写一堆isinstance()判断要么在文档里说明“这里可能是 str 或 int”全靠开发者的自觉和代码审查。后来类型提示Type Hints逐渐普及Union 就成了处理这类场景的正式工具。它不是什么运行时的新功能而是一种类型标注的语法用来告诉阅读代码的人包括你自己和静态类型检查工具“这个变量可能是这几种类型之一”。Union 到底是什么简单说Union 就是“或”的关系。在类型标注中Union[str, int]表示这个值要么是字符串要么是整数。它来自typing模块是 Python 3.5 引入类型提示时一起出现的。有意思的是很多人第一次接触 Union 时会觉得它很啰嗦——明明 Python 是动态类型语言为什么要多此一举其实这和 Python 的发展方向有关。随着项目规模变大动态类型的灵活性反而成了维护的负担。Union 这类类型提示工具就是在不牺牲 Python 简洁性的前提下为大型项目增加一点静态语言的可靠性。实际应用场景最常见的场景就是处理外部数据。比如解析 JSON 响应某个字段可能因为业务逻辑有时是字符串有时是数字。用 Union 标注后代码的意图就清晰多了fromtypingimportUniondefparse_user_age(age:Union[str,int])-int:ifisinstance(age,str):returnint(age)returnage另一个典型场景是兼容新旧版本接口。有些函数为了向后兼容既接受旧格式参数也接受新格式参数这时候用 Union 标注就特别合适。数据库操作中也经常见到 Union 的身影。查询结果可能是一个对象也可能是None当查询不到时这时候Union[User, None]就能准确表达这种可能性。不过更常见的写法是Optional[User]这其实是Union[User, None]的简写形式。使用时的细节从 Python 3.10 开始Union 有了更简洁的写法。原来的Union[str, int]可以写成str | int用竖线连接类型看起来更直观。这个改动虽然小但反映了 Python 社区的一个共识类型提示应该尽可能简洁不能因为添加类型提示就让代码变得难以阅读。使用 Union 时要注意覆盖所有可能的情况。如果标注了Union[str, int]那么代码就应该能妥善处理字符串和整数两种情况。静态类型检查工具如 mypy 会基于这些标注进行检查如果发现代码只处理了其中一部分情况就会给出警告。有时候会遇到嵌套的 Union比如Union[List[str], List[int]]。这种情况下可能需要更仔细地设计代码逻辑或者考虑是否可以用泛型来更好地表达意图。一些实践建议虽然 Union 很有用但不要滥用。如果一个函数参数可以是五六种不同类型那可能意味着函数设计得过于复杂了考虑拆分成多个函数可能是更好的选择。对于返回类型Union 要慎用。函数返回多种类型往往说明函数职责不够单一。当然有些场景确实需要比如查找函数找到返回对象找不到返回None这种模式已经很成熟了。在团队项目中类型提示的约定很重要。是坚持用 Union 标注所有可能类型还是允许在某些简单场景省略这个最好在项目初期就达成一致。个人经验是公共接口一定要完整标注内部辅助函数可以适当灵活。文档和类型提示要配合使用。类型提示说明了“是什么”文档则应该说明“为什么”——为什么这里需要 Union各种情况分别对应什么业务逻辑与其他方式的比较在没有 Union 之前人们常用Any来表示“任意类型”。但Any相当于放弃了类型检查而 Union 是在允许的多种类型中进行检查约束更强对代码质量的帮助也更大。有些场景下使用继承关系比 Union 更合适。如果几种类型有共同的基类那么标注基类类型可能更简洁。但 Union 更灵活不需要类型之间有继承关系。其他语言也有类似概念。TypeScript 的联合类型Union Types和 Python 的 Union 很像语法也类似。Java 等语言虽然没有直接的等价物但可以通过接口和继承达到类似效果只是写法会更复杂一些。最后要明白Union 只是类型提示不会影响运行时行为。即使标注了Union[str, int]实际传入列表也不会报错除非代码里有显式检查。它的价值主要体现在代码可读性和静态检查阶段。类型提示在 Python 中还在不断演进Union 只是其中的一部分。对于现代 Python 开发来说适当使用类型提示包括 Union已经成为一种专业习惯。它让代码的意图更清晰让协作更顺畅也让很多错误在编码阶段就能被发现。