python typing标准库
1. typing模块
官网文档:https://docs.python.org/zh-cn/3/library/typing.html1.1 typing模块简介
typing
模块是Python 3.5及以上版本中的一个标准模块,提供了对类型注解和类型提示的支持。该模块中包含了多个类、函数和类型别名等,用于描述函数、方法、变量等的输入和输出类型,以及自定义类型等。
在 Python 编程中,'typing'模块主要有以下特点:
- 支持类型注解,能够在编码时检测变量类型的错误;
- 提供强类型检查,能够防止类型错误,并提供丰富的类型提示信息;
- 支持常见的类型,如 `List`、`Tuple`、`Dict`、`Set` 等类型;
- 支持特定类型,如 `Union`、`Optional`、`Any` 等类型,可以满足复杂类型的需求;
- 支持类型操作符,如 `Callable`、`TypeVar`、`Generic` 等类型操作符,可以满足更多类型需求。
语法:
def 函数名(参数: 数据类型) -> 返回值类型: pass 变量名: 数据类型 = 值
1.2 typing模块基本使用
typing最常见的内置类型
Type Description int 整型 integer float 浮点数字 bool 布尔(int 的子类) str 字符 (unicode) bytes 8 位字符 object 任意对象(公共基类) List[str] 字符组成的列表 Tuple[int, int] 两个int对象的元组 Tuple[int, ...] 任意数量的 int 对象的元组 Dict[str, int] 键是 str 值是 int 的字典 Iterable[int] 包含 int 的可迭代对象 Sequence[bool] 布尔值序列(只读) Mapping[str, int] 从 str 键到 int 值的映射(只读) Any 具有任意类型的动态类型值 Union 联合类型 Optional 参数可以为空或已经声明的类型 Mapping 映射,是 collections.abc.Mapping 的泛型 MutableMapping Mapping 对象的子类,可变 Generator 生成器类型, Generator[YieldType、SendType、ReturnType] NoReturn 函数没有返回结果 Set 集合 set 的泛型, 推荐用于注解返回类型 AbstractSet collections.abc.Set 的泛型,推荐用于注解参数 Sequence collections.abc.Sequence 的泛型,list、tuple 等的泛化类型 TypeVar 自定义兼容特定类型的变量 Generic 自定义泛型类型 NewType 声明一些具有特殊含义的类型 Callable 可调用类型, Callable[[参数类型], 返回类型] 内置类型详解: #1.List var: List[int or float] = [2, 3.5] var: List[List[int]] = [[1, 2], [2, 3]] #2.Tuple person: Tuple[str, int, float] = ('Mike', 22, 1.75) #3.Dict、Mapping、MutableMapping Dict:字典,是 dict 的泛型;Mapping,映射,是 collections.abc.Mapping 的泛型。 Dict 推荐用于注解返回类型 Mapping 推荐用于注解参数 MutableMapping 则是 Mapping 对象的子类,在很多库中也经常用 MutableMapping 来代替 Mapping。 def size(rect: Mapping[str, int]) -> Dict[str, int]: return {'width': rect['width'] + 100, 'height': rect['width'] + 100} #4.Set、AbstractSet Set 推荐用于注解返回类型 AbstractSet 用于注解参数 def describe(s: AbstractSet[int]) -> Set[int]: return set(s) sequenceDiagram``` 在某些情况下,我们可能并不需要严格区分一个变量或参数到底是列表 list 类型还是元组 tuple 类型,我们可以使用一个更为泛化的类型,叫做 Sequence,其用法类似于 List ```py def square(elements: Sequence[float]) -> List[float]: return [x ** 2 for x in elements] #5.NoReturn 当一个方法没有返回结果时,为了注解它的返回类型,我们可以将其注解为 NoReturn def hello() -> NoReturn: print('hello') #6.Any 一种特殊的类型,它可以代表所有类型,静态类型检查器的所有类型都与 Any 类型兼容,所有的无参数类型注解和返回类型注解的都会默认使用 Any 类型 def add(a: Any) -> Any: return a + 1 #7.TypeVar 自定义兼容特定类型的变量 height = 1.75 Height = TypeVar('Height', int, float, None) def get_height() -> Height: return height #8.NewType 声明一些具有特殊含义的类型 Person = NewType('Person', Tuple[str, int, float]) person = Person(('Mike', 22, 1.75)) #9.Callable 可调用类型,它通常用来注解一个方法 def date(year: int, month: int, day: int) -> str: return f'{year}-{month}-{day}' 参数类型和返回值类型都要注解出来 def get_date_fn() -> Callable[[int, int, int], str]: return date #10.lambda类型标注 is_even: Callable[[int], bool] = lambda x: (x % 2 == 0) #11.Union 联合类型,Union[X, Y] 代表要么是 X 类型,要么是 Y 类型。 联合类型的联合类型等价于展平后的类型 Union[Union[int, str], float] == Union[int, str, float] Union[int] == int Union[int, str, int] == Union[int, str] Union[int, str] == Union[str, int] def process(: Union[str, Callable]): isinstance(, str): # str2fn and process pass isinstance(, Callable): () #12.Optional 参数可以为空或已经声明的类型, 即 Optional[X] 等价于 Union[X, None]。 不等价于可选参数,当它作为参数类型注解的时候,不代表这个参数可以不传递了,而是说这个参数可以传为 None # 当一个方法执行结果, 如果执行完毕就不返回错误信息, 如果发生问题就返回错误信息 from typing import Optional def judge(result: bool) -> Optional[str]: if result: return 'Error Occurred' #13.Generator 生成器类型, 声明方式, 其后的中括号紧跟着三个参数,分别代表 YieldType、SendType、ReturnType YieldType: yield 关键字后面紧跟的变量的类型 SendType: yield 返回的结果的类型就是 SendType, 可为None ReturnType: 最后生成器 return 的内容类型, 可为None def echo_round() -> Generator[int, float, str]: sent = yield 0 while sent >= 0: sent = yield round(sent) return 'Done'
#使用语法: def 函数名(参数: 数据类型) -> 返回值类型: pass 变量名: 数据类型 = 值 #typing详解: 1.1 变量声明 # 声明变量类型的类型的方式,python 3.6 + age: int = 1 # 在 Python 3.5 及更低版本中,您可以改用类型注释 # 同上效果 age = 1 # type: int # 无需初始化变量即可对其进行注释 a: int # ok(但不能调用,name 'a' is not defined,直到被赋值) # 在条件分支中很有用 child: bool if age < 18: child = True else: child = False #使用内置类型声明变量 from inner_module_def_datastruct import WEIXIN_URL, AUTHOR , AGE from typing import List, Tuple, Dict, Set, Optional # 可以使用typing中的基本类型来声明变量的类型,如int,float,bool,str等 # 对于简单的 Python 内置类型,只需使用类型的名称 x: int = 1 x: float = 1.0 x: bool = True x: str = "test" x: bytes = b"test" # 对于 collections ,类型名称用大写字母表示,并且collections 内类型的名称在方括号中 x: List[int] = [1] x: Set[int] = {6, 7} # 与上述相同,但具有类型注释语法 x = [1] # type: List[int] # 对于映射,需要键和值的类型 x: Dict[str, float] = {'field': 2.0} # 对于固定大小的元组,指定所有元素的类型 x: Tuple[int, str, float] = (3, "yes", 7.5) # 对于可变大小的元组,使用一种类型和省略号 x: Tuple[int, ...] = (1, 2, 3) # 使用 Optional[] 表示可能为 None 的值 x: Optional[str] = some_function() # Mypy 理解 if 语句中的值不能为 None if x is not None: print(x.upper()) # 如果由于某些不变量而使值永远不能为 None,请使用断言 assert x is not None print(x.upper()) name: str = AUTHOR age: int = 25 height: float = 1.75 active: bool = True blog: str = WEIXIN_URL numbers_list: List[int] = [1, 2, 3, 4, 5] numbers_tuple: Tuple[int, float, str] = (1, 1.0, "1") numbers_dict: Dict[str, int] = {"one": 1, "two": 2, "three": 3} numbers_set: Set[str] = {"one", "two", "three"} def add_numbers(numbers: List[int]) -> int: return sum(numbers) print(add_numbers(numbers_list)) 1.2 函数声明 from typing import Callable, Iterator, Union, Optional, List # 注释函数定义的方式 def stringify(num: int) -> str: return str(num) # 指定多个参数的方式 def plus(num1: int, num2: int) -> int: return num1 + num2 # 在类型注释后为参数添加默认值 def f(num1: int, my_float: float = 3.5) -> float: return num1 + my_float # 注释可调用(函数)值的方式, lambda 可以此方法 x: Callable[[int, float], float] = f # 产生整数的生成器函数安全地返回只是一个 # 整数迭代器的函数,因此这就是我们对其进行注释的方式 def g(n: int) -> Iterator[int]: i = 0 while i < n: yield i i += 1 # 可以将功能注释分成多行 def send_email(address: Union[str, List[str]], sender: str, cc: Optional[List[str]], bcc: Optional[List[str]], subject='', body: Optional[List[str]] = None ) -> bool: 1.3 混杂结构 以下是一些复杂结构的用法: from typing import Union, Any, List, Optional, cast # Union 表示可能是以下几种类型 x: List[Union[int, str]] = [3, 5, "test", "fun"] # 不知道类型或它太动态而无法为它编写类型,请使用 Any x: Any = mystery_function() # 如果使用空容器或“无”初始化变量 # 类型注解帮助 mypy 获知类型信息 x: List[str] = [] x: Optional[str] = None # 每个位置 arg 和每个关键字 arg 均为 str def call(self, *args: str, **kwargs: str) -> str: request = make_request(*args, **kwargs) return self.do_api_query(request) # cast 可以转换类型 a = [4] b = cast(List[int], a) # 正常通过 c = cast(List[str], a) # 正常通过 (运行是不做检查,无影响) # 如果要在类上使用动态属性,请使其覆盖 “ __setattr__” # 或 “ __getattr__”。 # # "__setattr__" 允许动态分配名称 # "__getattr__" 允许动态访问名称 class A: # 如果 x 与“值”属于同一类型,则这将允许分配给任何 A.x # (使用“value: Any”以允许任意类型) def __setattr__(self, name: str, value: int) -> None: ... # 如果 x 与返回类型兼容,则将允许访问任何 A.x def __getattr__(self, name: str) -> int: ... a.foo = 42 # Works a.bar = 'Ex-parrot' # Fails type checking 1.4 用户定义的泛型类型 from typing import TypeVar, Generic from logging import Logger T = TypeVar('T') class LoggedVar(Generic[T]): def __init__(self, value: T, name: str, logger: Logger) -> None: self.name = name self.logger = logger self.value = value def set(self, new: T) -> None: self.log('Set ' + repr(self.value)) self.value = new def get(self) -> T: self.log('Get ' + repr(self.value)) return self.value def log(self, message: str) -> None: self.logger.info('%s: %s', self.name, message) Generic[T] 是定义类 LoggedVar 的基类,该类使用单类型参数 T。在该类体内,T 是有效的类型。 1.5 lambda的类型标注 由于类型注解的语法和 lambda 的语法冲突,因此不能直接对 lambda 做类型注解,但我们可以将 lambda 传给一个变量,通过对这个变量做 lambda,达到相同的目的。以下对 lambda 的几个例子: from typing import Callable # is_even 传入 int 返回布尔 is_even: Callable[[int], bool] = lambda x: (x % 2 == 0) # func 传入两个字符串,返回 int func: Callable[[str, str], int] = lambda var1, var2: var1.index(var2) 1.6 鸭子类型 在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。 在典型的 Python 代码中,许多可以将列表或 dict 作为参数的函数只需要将其参数设为“类似于列表”(list-like)或“类似于 dict”(dict-like)即可。 “类似列表”或“类似字典”(或类似其他的东西)的特定含义被称为「鸭子类型」,并且标准化了在惯用Python中常见的几种鸭子类型。 from typing import Mapping, MutableMapping, Sequence, Iterable, List, Set # 将 Iterable 用于一般可迭代对象(for 中可用的任何东西) # 以及需要序列(支持 len 和 __getitem__ 的序列) def f(ints: Iterable[int]) -> List[str]: return [str(x) for x in ints] f(range(1, 3)) # Mapping 映射描述了一个我们不会经常变化的 # 类似 dict 的对象(带有 __getitem__) # 而 MutableMapping 则描述了一个对象(带有 __setitem__) def f(my_mapping: Mapping[int, str]) -> List[int]: my_mapping[5] = 'maybe' # mypy 会引发错误 return list(my_mapping.keys()) f({3: 'yes', 4: 'no'}) def f(my_mapping: MutableMapping[int, str]) -> Set[str]: my_mapping[5] = 'maybe' # mypy 正常执行 return set(my_mapping.values()) f({3: 'yes', 4: 'no'}) 1.7 类中的应用 class MyClass: # 在类主体中声明实例变量 attr: int # 具有默认值的实例变量 charge_percent: int = 100 # __init__ 方法不返回任何内容,因此返回 None def __init__(self) -> None: ... # 对于实例方法,省略 self 的类型 def my_method(self, num: int, str1: str) -> str: return num * str1 # 用户定义的类作为注释中的类型有效 x: MyClass = MyClass() # 可以使用 ClassVar 批注来声明类变量 class Car: seats: ClassVar[int] = 4 passengers: ClassVar[List[str]] # 可以在 __init__ 中声明属性的类型 class Box: def __init__(self) -> None: self.items: List[str] = [] 1.8 装饰器 from typing import Any, Callable, TypeVar F = TypeVar('F', bound=Callable[..., Any]) def bare_decorator(func: F) -> F: ... def decorator_args(url: str) -> Callable[[F], F]: ... 1.9 Noreturn NoReturn,当一个方法没有返回结果时,为了注解它的返回类型,我们可以将其注解为 NoReturn,例如: def fun() -> NoReturn: print('Hi!')
1.1 类型别名 要定义一个类型别名,可以将一个类型赋给别名。类型别名可用于简化复杂类型签名,同时类型别名适用于简化复杂的类型签名 #!/usr/bin/env python # -*- coding: UTF-8 -*- from typing import Sequence,Dict,Tuple ConnectionOptions = Dict[str, int] # 表示字典中的键为字符串类型,值为整型 Address = Tuple[str, int, ...] # 表示元组的第一个数据为字符串,第二个数据为整型,里面只能存储两个数据,有省略号表示里面可以添加n个整型数据 Server = Tuple[Address, ConnectionOptions] def broadcast_message(message: str, servers: Sequence[Server] # 表示一个序列对象里面存储了[tuple[tuple[str, int], dict[str, int]]] ) -> None: # 返回值为空 ... broadcast_message("a", [(("a", 1, 2), {"a": 1})]) 1.2 NewType 使用NewType辅助函数创建不同的类型,静态类型检查器会将新类型视为它是原始数据的子类,相当于C++里面的'typedef' from typing import NewType UserId = NewType('UserId', int) # 其不会创建一个新的类或引入其他内存,只是做一个约束作用 def name_by_id(user_id: UserId) -> str: ... name_by_id(42) # Fails type check name_by_id(UserId(42)) # OK num = UserId(5) + 1 # type: int,可以进行对应数据类型的操作 同时,可以嵌套创建,即可以基于NewType创建NewType 1.3 可调用对象 Callable[[Arg1Type, Arg2Type], ReturnType] 如,实现一个互斥锁的装饰器 from collections.abc import Callable # 注意要使用Concatenate和ParamSpec就必须使用这个模块里面的Callable from threading import Lock from typing import TypeVar from pip._vendor.typing_extensions import Concatenate, ParamSpec # 导入typing的扩展 P = ParamSpec('P') # 里面有args和kwargs参数 R = TypeVar('R') # 自定义数据类型 my_lock = Lock() # 创建一个互斥锁 def with_lock(f: Callable[Concatenate[Lock, P], R]) -> Callable[P, R]: '''一个提供互斥锁,使得线程安全的装饰器''' def inner(*args: P.args, **kwargs: P.kwargs) -> R: return f(my_lock, *args, **kwargs) return inner @with_lock def sum_threadsafe(lock: Lock, numbers: list[float]) -> float: '''Add a list of numbers together in a thread-safe manner.''' with lock: return sum(numbers) # We don't need to pass in the lock ourselves thanks to the decorator. print(sum_threadsafe([1.1, 2.2, 3.3])) 2. 泛型支持 typing模快最基本的支持有Any ,Tuple,Callable,TypeVar 和 Generic类型组成 2.1 集合类型: from typing import ( List, # list的泛型版本。用于注释返回类型。要注释参数,最好使用抽象集合类型,如Sequence或Iterable Set, # set的泛型版本 Dict # dict 的泛型版本。对标注返回类型比较有用。如果要标注参数的话,使用如 Mapping 的抽象容器类型是更好的选择 ) 2.2 抽象基类 from typing import ( Mapping, # 要注释函数参数中的Key-Value类型时,推荐使用的抽象集合类型 Sequence, # 要注释函数参数中的序列例如列表类型时,推荐使用的抽象集合类型 Iterable # 要注释函数参数中的迭代类型时,推荐使用的抽象集合类型 ) 2.3 泛型 TypeVar:其就像C++里面的template一样 from typing import ( Sequence, TypeVar # 限制多个变量为同一个数据类型 ) T = TypeVar('T') # Can be anything A = TypeVar('A', str, bytes) # Must be str or bytes def repeat(x: T, n: int) -> Sequence[T]: """Return a list containing n references to x.""" return [x] * n def longest(x: A, y: A) -> A: """Return the longest of two strings.""" return x if len(x) >= len(y) else y AnyStr AnyStr是一个字符串和字节类型的特殊类型变量AnyStr = TypeVar('AnyStr', str, bytes),它用于可以接受任何类型的字符串而不允许不同类型的字符串混合的函数 2.4 Any 特殊类型,表明类型没有任何限制,每一个类型都对 Any 兼容,Any 对每一个类型都兼容 Any 是一种特殊的类型。静态类型检查器将所有类型视为与Any兼容,反之亦然, Any也与所有类型相兼容。 这意味着可对类型为 Any 的值执行任何操作或者方法调用并将其赋值给任意变量 如下所示,将 Any 类型的值赋值给另一个更具体的类型时,Python不会执行类型检查。例如,当把 a 赋值给 s 时,即使 s 被声明为 str类型,在运行时接收到的是 int 值,静态类型检查器也不会报错 from typing import ( Any, NoReturn, # 表示函数没有返回值 ) def test(s: Any) -> NoReturn: s.item() # 不会检测s里面是否有item()属性 def test_(s: object) -> NoReturn: s.item() # 会检测s里面是否有item属性 当参数无类型是,默认为Any类型 2.5 特殊形式 2.5.1 Type 一个注解为 C 的变量可以接受一个类型为 C 的值。相对地,一个注解为 Type[C] 的变量可以接受本身为类的值 。 更精确地说它接受 C的类对象 from typing import Type class User: ... class BasicUser(User): ... # Accepts User, BasicUser, ... def make_new_user(user_class: Type[User]) -> User: return user_class() print(make_new_user(User)) 2.5.2 Union 联合类型: Union[X, Y]意味着:要么是 X,要么就是 Y。定义一个联合类型,需要注意的有: 参数必须是类型,而且必须至少有一个参数。 能继承或者实例化一个联合类型。 Union[X, Y]不能写成 Union[X][Y] 。 可以使用 Optional[X] 作为Union[X, None]的缩写- 联合类型的联合类型会被展开打平 仅有一个参数的联合类型会坍缩成参数自身,比如: Union[int] == int # The constructor actually returns int 多余的参数会被跳过,比如: Union[int, str, int] == Union[int, str] 在比较联合类型的时候,参数顺序会被忽略,比如: Union[int, str] == Union[str, int] 2.5.3 Optional 可选类型,Optional[X] 等价于Union[X, None] 2.5.4 Tuple 元组类型,Tuple[X, Y] 标注了一个二元组类型,其第一个元素的类型为 X 且第二个元素的类型为Y。空元组的类型可写作 Tuple[()] 为表达一个同类型元素的变长元组,使用省略号字面量,如Tuple[int, ...]。单独的一个 Tuple 等价于 Tuple[Any, ...],进而等价于tuple 示例: Tuple[int, float, str]表示一个由整数、浮点数和字符串组成的三元组 2.5.4 Callable 可调用类型;Callable[[int], str]是一个函数,接受一个 int 参数,返回一个str。 下标值的语法必须恰为两个值:参数列表和返回类型。参数列表必须是一个类型和省略号组成的列表;返回值必须是单一一个类型 不存在语法来表示可选的或关键词参数,这类函数类型罕见用于回调函数。Callable[..., ReturnType](使用字面省略号)能被用于提示一个可调用对象,接受任意数量的参数并且返回 ReturnType。单独的 Callable 等价于Callable[..., Any],并且进而等价于 collections.abc.Callable
typing
模块中常用的几个类、函数和类型别名的介绍和用法示例如下:
需要注意的是,typing
模块中的类、函数和类型别名等仅用于类型注解和类型提示,并不会对Python代码的实际运行产生影响。使用typing
模块可以提高代码的可读性和可维护性,同时也可以帮助IDE等工具进行类型检查和类型推断等操作。
补充:
Iterable与Iterator的区别:
Iterable和Iterator都是Python中typing模块中的类型别名,用于表示可迭代对象和迭代器。它们的区别在于:
Iterable表示一个可迭代对象的类型,即可以被for循环遍历的对象。例如,列表、元组、字典、集合等都属于可迭代对象。Iterable的类型注解形式为Iterable[T],其中T表示可迭代对象中元素的类型。Iterable类型的对象可以使用iter()函数来获取一个迭代器。Iterator表示一个迭代器的类型,即可以通过next()函数一个一个地访问元素的对象。例如,使用iter()函数得到的迭代器对象就是一个迭代器。Iterator的类型注解形式为Iterator[T],其中T表示迭代器中元素的类型。迭代器对象可以使用next()函数来获取下一个元素,如果没有下一个元素,则会抛出StopIteration异常。 简单来说,可迭代对象是一类具有__iter__方法的对象,该方法返回一个迭代器对象。而迭代器对象是一类具有__next__方法的对象,该方法返回下一个元素,如果没有下一个元素则抛出StopIteration异常。因此,可以使用iter()函数将可迭代对象转换为迭代器对象,然后使用next()函数逐个访问元素。需要注意的是,迭代器对象是一次性的,即迭代完一次后不能再次迭代,需要重新获取迭代器对象才能再次迭代。
FrozenSet与Set的区别
FrozenSet和Set都是Python中typing模块中的类型别名,用于表示集合类型,它们的区别在于可变性:
Set表示一个可变的集合类型,即可以随时添加、删除元素的集合。Set的类型注解形式为Set[T],其中T表示集合中元素的类型。
FrozenSet表示一个不可变的集合类型,即一旦创建之后就不可以再添加或删除元素。FrozenSet的类型注解形式为FrozenSet[T],其中T表示集合中元素的类型。因为FrozenSet是不可变的,所以它可以作为字典的键或集合的元素。 需要注意的是,Set和FrozenSet都是可迭代对象,可以使用for循环遍历集合中的元素。另外,集合类型中的元素是无序的,即不能通过下标来访问集合中的元素。如果需要有序的集合类型,可以使用List或Tuple类型。
TypeVar与"…"的相同与区别
TypeVar和...都是Python中typing模块中的工具,用于在类型注解中引入泛型类型,它们的相同点和不同点如下:
相同点:
都是typing模块中用于引入泛型类型的工具;
都可以用于函数、方法、类等的参数和返回值的类型注解中;
都可以用于指定任意类型,增加代码的灵活性和复用性。
不同点:
TypeVar用于定义泛型类型变量,表示一个未知的类型,可以在需要的地方进行具体化。泛型类型变量可以用于类、函数、方法等的参数和返回值的类型注解中,可以用于指定任意类型。
...用于表示不定参数类型,即表示函数的参数可以是任意数量、任意类型的参数。Callable[..., int]表示一个可以接受任意数量、任意类型的参数,并返回int类型的函数类型。不定参数类型可以用在函数的类型注解中,用于表示函数可以接受任意数量、任意类型的参数,增加代码的灵活性。 因此,TypeVar和...都是用于引入泛型类型的工具,但是它们的用途不同,TypeVar用于定义泛型类型变量,而...用于表示不定参数类型。