类型注解与typing模块

  |  

摘要: Python 中的类型注解特性

【对算法,数学,计算机感兴趣的同学,欢迎关注我哈,阅读更多原创文章】
我的网站:潮汐朝夕的生活实验室
我的公众号:算法题刷刷
我的知乎:潮汐朝夕
我的github:FennelDumplings
我的leetcode:FennelDumplings


类型注解

类型注解是 Python 比较新的一个特性。用了类型注解的代码有以下两个好处

  1. 可以明晰变量、函数参数、函数返回值的类型
  2. 可以对代码进行类型检查,例如 mypy 就是一个相关的工具

函数参数和返回值类型注解的例子

1
2
def func(s:str) -> str:
return "Hello, " + s

变量的类型注解的例子

1
str_value:str = "algorithm"

typing 模块

Python 中的类型非常多,int, str 等简单的类型可以直接按上面的例子那样写,但是要在类型注解中写难一点的类型就不会了,比如 list,dict,自定义类型等等。

typing库是一个帮助我们实现类型注解的库,里面包含了很多类型。在当前时间点 typing 中包含的类型总结在了文末的表格中(以后typing库可能会更新)。

下面我们针对几个常见的类型给出一些例子。

List

1
2
3
4
5
6
from typing import List

Vector = List[float]

def func(x: float, vec: Vector) -> Vector:
return [x * a for a in vec]

Dict

1
2
3
4
5
6
7
8
from typing import Dict

memo: Dict[int, int] = {0: 0, 1: 1}

def fib(n: int) -> int:
if n not in memo:
memo[n] = fib(n - 1) + fib(n - 2)
return memo[n]

Tuple

1
2
3
4
5
6
7
8
from typing import Dict, Tuple, List

ConnectionOptions = Dict[str, str]
Address = Tuple[str, int]
Server = Tuple[Address, ConnectionOptions]

def broadcast_message(message: str, servers: List[Server]) -> None:
...

NewType

NewType 辅助函数创造不同的类型,例如

1
2
3
4
from typing import NewType

UserId = typing.NewType("UserId", int)
my_id = UserId(123456)

用 NewType 创造的类型会被静态类型检查器视为它的原始类型的子类。也就是说在上面的代码中,int 不能用于 UserId,而 UserId 可以用于 int(即可以用 int 类型变量的所有操作来使用 UserId 变量,但结果返回的都是 int),例如

1
z = UserId(x) + UserId(y)

在运行时,NewType 它返回一个函数,该函数返回其参数。由于 typing.NewType 返回的函数只是直接返回所传的任何参数,因此 UserId(x) 并不会创建新类。也就是说,上面的代码中 my_id 实际还是 int 类型。这个特性会产生以下一些现象。

  • x is UserId(x) 总是 True。
  • UserId 不能用于派生新类。class A(UserId): 会报错。
  • 可以基于 UserId 再衍生新的 NewType。NewUserId = typing.NewType("NewUserId", UserId)

Callable

回调函数的类型注释大致是这样 Callable[[Arg1Type, Arg2Type],ReturnType],或者 Callable[..., ReturnType],例如

1
2
3
4
5
6
from typing import Callable

def async_query(on_success: Callable[[int], None])
,on_error: Callable[[int, Exception], None]
) -> None:
...

Mapping

关联容器

1
2
3
4
from typing import Mapping

mapping: Mapping[str, str]
mapping = {"a": "xyz"}

Sequence

序列容器

1
2
3
4
5
6
7
from typing import Sequence

class Employee:
...

def notify_by_email(employees: Sequence[Employee]) -> None:
...

TypeVar

类型集合

1
2
3
4
5
from typing import TypeVar

AllType = TypeVar("AllType") # 任意类型都可以
TypeSet = TypeVar("TypeSet", str, int)
val: TypeSet = 1

通过 TypeVar 可以传给 Sequence

1
2
3
4
T = TypeVar("T")

def get_first(l: Sequence[T]) -> T:
return l[0]

类型集合可以被继承

1
2
3
T = TypeVar("T")
class MyDict(Mapping[str, T]):
...

Generic

自定义泛型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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, v: T) -> None:
self.value = v

以上代码中,Generic[T] 是 LoggedVar 的基类。

Generic 的作用:通过 Generic 基类使用元类 (metaclass) 定义 __getitem__() 使得下面代码中的 LoggedVar[int] 是有效类型。

Generic 的元类是 abc.ABCMeta 的子类,泛型类是包含抽象方法或属性的 ABC 类

1
2
3
4
5
from typing import Iterable

def init_nums(loggervars: Iterable[LoggedVar[int]]) -> None:
for num in nums:
num.set(0)

用 Generic 可以实现多继承

1
2
3
T = TypeVar("T")
class LinkedList(Sized, Generic[T]):
...

Iterable

不指定类型参数,则假定每个位置都是 Any

1
2
class MyIterable(Iterable): # 等效于 Iterable[Any]
...

Generator

生成器

1
2
3
4
5
6
7
8
9
10
from typing import Generator

def fib(n: int) -> Generator[int, None, None]:
yield 0
if n > 0: yield 1
last: int = 0 # fib(0)
nxt: int = 1 # fib(1)
for _ in range(1, n):
last, nxt = nxt, last + nxt
yield nxt

typing.Any

Any 是一种特殊类型,静态类型检查器将每个类型视为与任何类型兼容。可对类型为 Any 的值执行任何操作或方法调用,并将其赋值给任何变量。所有返回值无类型或形参无类型的函数将隐式地默认使用 Any 类型。Any 值的类型是动态定义的。

Object 类型可以与 Any 对比。所有的类型都是 object 的子类型。当一个值的类型是 object 的时候,静态类型检查器会拒绝对它的几乎所有的操作。把它赋值给一个指定了类型的变量(或者当作返回值)是一个类型错误。


typing 包含的类型

类型 typing 中对应的类型
AbstractSet typing.AbstractSet
Any typing.Any
AnyStr typing.AnyStr
AsyncContextManager typing.AbstractAsyncContextManager
AsyncGenerator typing.AsyncGenerator
AsyncIterable typing.AsyncIterable
AsyncIterator typing.AsyncIterator
Awaitable typing.Awaitable
ByteString typing.ByteString
Callable typing.Callable
ClassVar typing.ClassVar
Collection typing.Collection
Container typing.Container
ContextManager typing.AbstractContextManager
Coroutine typing.Coroutine
Counter typing.Counter
DefaultDict typing.DefaultDict
Deque typing.Deque
Dict typing.Dict
FrozenSet typing.FrozenSet
Generator typing.Generator
Hashable typing.Hashable
ItemsView typing.ItemsView
Iterable typing.Iterable
Iterator typing.Iterator
KeysView typing.KeysView
List typing.List
Mapping typing.Mapping
MappingView typing.MappingView
MutableMapping typing.MutableMapping
MutableSequence typing.MutableSequence
MutableSet typing.MutableSet
NoReturn typing.NoReturn
Optional typing.Optional
Reversible typing.Reversible
Sequence typing.Sequence
Set typing.Set
Sized typing.Sized
TYPE_CHECKING False
Tuple typing.Tuple
Type typing.Type
Union typing.Union
ValuesView typing.ValuesView

Share