首页IT科技python类型注解 知乎(Python 类型注解)

python类型注解 知乎(Python 类型注解)

时间2025-08-04 20:55:11分类IT科技浏览6358
导读:在Python语言发展的过程中,PEP提案发挥了巨大的作用,如PEP 3107 和 PEP 484提案,分别给我们带来了函数注解(Function Annotations)和类型提示(Type Hints)的功能。...

在Python语言发展的过程中               ,PEP提案发挥了巨大的作用                        ,如PEP 3107 和 PEP 484提案        ,分别给我们带来了函数注解(Function Annotations)和类型提示(Type Hints)的功能               。

PEP 3107:定义了函数注解的语法           ,允许为函数的参数和返回值添加元数据注解                        。

PEP 484:按照PEP 3107函数注解的语法                        ,从Python语法层面全面支持类型提示            ,类型提示可以是内置类型               、内置类                        、抽象基类        、types模块中提供的类型和开发人员自定义的类        。

另外 PEP 526, PEP 544, PEP 586, PEP 589, PEP 591 这些东西对 PEP 3107 和 PEP 484 进行了补充       ,比如添加了变量注释                        ,字面量注释这些东西           。

需要注意的是                ,类型提示仅有提示的作用   ,这里的提示是指用户阅读Python代码的时候的提示                       ,仅在语法层面支持                    ,对代码的运行没有任何影响,Python 解释器在运行代码的时候会忽略类型提示                   ,也就是说                        ,Python的类型提示仅是为了提升代码可读性    ,一定程度上缓解"动态语言一时爽               ,代码重构火葬场"的尴尬                        。

下面将函数注解和类型提示                        ,统称为类型注解            。

类型注解优点

1           、可以使Python拥有部分静态语言的特性        ,利用类型注解可以实现一种类似类型声明的效果           ,提升代码的可读性及后续的可维护性       。

2                        、类型注解可以让IDE(如pycharm)像静态语言那样分析我们的代码                        ,及时给我们相应的提示            ,如下图对比:

VS

3            、多多使用类型注解       ,不仅可以让Python拥有强类型语言的严谨                        ,还能保持Python作为动态类型语言的灵活性                        。

普通变量类型注解

在声明变量时                ,变量的后面可以加一个冒号   ,后面再写上变量的类型                       ,如 int       、list 等等                    ,以此实现类型注解                。

a: int = 22 b: str = "name" c: float = 55.5 d: bool = True e: list = [1, 2, 3] f: set = {1, 2, 3} g: dict = {"name": "ming", "age": 22} h: tuple = (1, 2, 3) i: bytes = bworld j: bytearray = bytearray("world")

函数参数及返回值类型

函数参数的类型声明就是冒号+类型即可,和普通变量类型声明没区别   。

函数返回值的类型声明是用箭头指向具体的类型                   ,如果是返回值有多个                        ,使用元组包裹即可(因为函数的多个返回值就是以元组形式返回的)    ,需要注意的是               ,箭头左右两边都要留有空格                       。

def handler(a: int, b: int) -> int: return a + b def handler2(a: int, b: int, *args: int) -> int: return a + b + sum(args) def handler3(a: int, b: int, *args: int, **kwargs: int) -> (int, str): return a + b + sum(args) + sum(kwargs.values()), ""

typing模块

typing模块的加入不会影响程序的运行                        ,也不会报正式的错误        ,pycharm支持检测基于typing注解的错误           ,不符合规定类型注解时会出现黄色警告                        ,但不会影响程序运行                    。

容器类型 & 复合类型

列表                        、字典                、元组等包含元素的复合类型            ,用简单的 list       ,dict                        ,tuple 不能够明确说明内部元素的具体类型。

此外                ,Python本身就是动态类型的语言   ,如果我们强制使用某种类型                       ,一定程度上会丧失Python作为动态语言的优势                    ,因此 typing 模块提供了一种复合类型注解的语法,即一个参数即可以是类型A                   ,也可以是类型B或者类型C

from typing import Dict, List, Set, Tuple, Union # 字典 d: Dict[str, int] = {"a": 1, "b": 2} d1: Dict[str, int or str] = {"a": 1, "b": "2"} # 使用or表示支持多个类型 # 列表 l: List[int] = [1, 2, 3] l1: List[int or str] = [1, 2, "3"] # 元组 t: Tuple[str, int] = ("a", 1) # 代表了构成元组的第一个元素是 str 类型                        ,第二个元素是 int 类型 t1: Tuple[str, ...] = ("a", "b", "c", "d", "e", "f", "g") # 代表接受多个 str 类型的元素 t2: Tuple[str or int, ...] = ("a", "b", 2) # 代表接受多个 str 或 int 类型的元素 # 集合 s: Set[int] = {1, 2, 3, 4} s1: Set[Union[int, str, float]] = {1, "2", 3.333, 4} # Union 同 or

TypedDict

TypedDict声明一个字典类型    ,该类型期望它的所有实例都有一组固定的keys               ,其中每个key都与对应类型的值关联                   。

from typing import TypedDict class Student(TypedDict): name: str age: int height: float s1: Student = { "name": "xiao ming", "age": 22, "height": 55.5 } s2: Student = { "name": "xiao hong", "age": 21, }

可以看出                        ,pycharm也会警告我们字典实例中缺失的key                        。

同时        ,在我们生成字典实例的时候           ,pycharm也会给我们key的提示    。

类型别名

类型别名是通过将类型分配给别名来定义的                        ,类型别名可用于简化复杂类型提示               。

from typing import Union Number = Union[int, float] def process(v: Number) -> Number: return v x: Number = 2 y: Number = 2.2 process(x) process(22) # 类型检查成功            ,类型别名和原始类型是等价的

NewType

使用NewType辅助类来创建不同的类型

from typing import NewType Number = NewType("Number", int) def process(v: Number) -> Number: return v x: Number = Number(22) process(x) process(22) # 类型检查异常:Expected type Number, got int instead # 原因就是NewType创建的是原始类型的“子类型                ”

因此       ,类型别名 和 NewType 具体使用哪个                        ,要视情况而定                ,不知道使用哪个   ,可以先使用类型别名                        。

NoReturn

当一个方法没有返回结果时                       ,为了注解它的返回类型                    ,我们可以将其注解为 NoReturn        。

因为Python 的函数运行结束时隐式返回 None ,这和真正的无返回值是有区别的           。

from typing import NoReturn def process() -> NoReturn: pass

可选类型:Optional

使用 Optional[] 表示可能为 None 的值

from typing import Optional def handler(x: int) -> Optional[int]: if x % 2 == 0: return x

可调用对象:Callable

若一个变量类型是可调用函数                   ,则可以用 Callable[[Arg1Type, Arg2Type], ReturnType] 实现类型提示

from typing import Optional, Callable def handler(x: int) -> Optional[int]: if x % 2 == 0: return x def handler2(func: Callable[[int], Optional[int]]): pass handler2(handler)

字面量:Literal

指示相应的变量或函数参数只接收与提供的字面量(或多个字面量之一)等效的值                        ,可以理解为规定了某个参数或变量的所有枚举值                        。

from typing import Literal, NoReturn Mode = Literal["r", "w"] def process(mode: Mode) -> NoReturn: pass process("s")

可以看出    ,pycharm检查出了我们输入的值并不符合字面量规定的值               ,进而出现了黄色警告            。

Any

是一种特殊的类型                        ,每种类型都视为与Any兼容        ,同样           ,Any也与所有类型兼容       。可以对Any类型的值执行任何操作或方法调用                        ,并将其分配给任何变量                        。将Any类型的值分配给更精确的类型(more precise type)时            ,不会执行类型检查       ,所有没有返回类型或参数类型的函数都将隐式地默认使用Any                。

使用Any                        ,说明值是动态类型   。

把所有的类型都注解为 Any 将毫无意义                ,因此 Any 应当尽量少使用

from typing import Any def foo() -> Any: pass

抽象基类

# 在某些情况下   ,我们可能并不需要严格区分一个变量或参数到底是列表 list 类型还是元组 tuple 类型 # 可以使用一个更为泛化的类型                       ,叫做 Sequence                    ,其用法类似于 List class typing.Sequence(Reversible[T_co], Collection[T_co]) # collections.abc.Iterator的泛型版本 # 注释函数参数中的迭代类型时,推荐使用的抽象集合类型 class typing.Iterable(Generic[T_co]) def print_iterable(x: Iterable): for i in x: print(i) # collections.abc.Mapping的泛型(generic)版本 # 注释函数参数中的Key-Value类型时                   ,推荐使用的抽象集合类型 class typing.Mapping(Sized, Collection[KT], Generic[VT_co])

泛型:TypeVar

先抛出问题:

假设有一个函数                        ,要求它既能够处理字符串    ,又能够处理数字                       。那么你可能很自然地想到了 Union                ,如下:

from typing import Union AddValue = Union[int, str] def add(a: AddValue, b: AddValue) -> AddValue: return a + b if __name__ == "__main__": print(add(1, 2)) # 类型检查通过                        ,输出 3 print(add("1", "2")) # 类型检查通过        ,输出 12 print(add("1", 2)) # 类型检查通过           ,报错 TypeError: can only concatenate str (not "int") to str

在类型检查通过的情况下                        ,我们完成并运行了这段代码            ,可是代码却报错了!

原因就是我们的初衷是数字和数字相加实现求和       ,字符串和字符串相加实现拼接                        ,没有考虑到字符串与数字混用的问题                ,从而引发错误                    。

根据以上问题   ,我们可以引入泛型来解决这个问题:

from typing import TypeVar AddT = TypeVar("AddT", int, str) def add(a: AddT, b: AddT) -> AddT: return a + b if __name__ == "__main__": print(add(1, 2)) # 类型检查通过                       ,输出 3 print(add("1", "2")) # 类型检查通过                    ,输出 12 print(add("1", 2)) # 类型检查失败,pycharm告警 Expected type str (matched generic type AddT), got int instead

"""

通过告警                   ,我们提前发现了混用类型的问题                        ,避免了程序运行时发生异常的可能。

"""

泛型很巧妙地对类型进行了参数化    ,同时又保留了函数处理不同类型时的灵活性                   。

引用

1   、Python 标准库 typing 类型注解标注

2                       、Python类型注解               ,你需要知道的都在这里了

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
物联网智能家居比赛(物联网省/国赛AIOT智能家居全流程演示) themeforest主题(ThemeForest多功能WooCommerce主题推荐:Oneclick(woocommerce主题制作))