1. 前言
type hints(类型标注、类型注解、类型提示)是指在代码中涉及到数据交互的地方,提供数据类型的注解(显式的说明)。类型注解可以帮助第三方IDE工具(PyCharm)对代码进行类型推断,协助做代码提示;能帮助开发者自身对变量进行类型标注。类型注解可分为:变量的类型注解、函数(方法)形参列表和返回值的类型注解。总而言之,加上注解之后,我们可以直观的看出变量、函数(方法)参数列表和返回值到底是个什么东西(什么类型)。
2. 简单的类型注解
简单的类型注解(基本类型注解):可以使用int、str和list等基本类型来注解变量、函数参数和返回值的类型。
2.1 变量的类型注解
为变量设置类型注解,语法格式为:变量: 类型
,代码示例如下。
num: int = 10
s: str = "hello"
my_list: list = [1,2,3,4]
my_tuple: tuple = (1,2,3)
class Student:
pass
stu: Student = Student()
除了使用变量: 类型
这种格式外,还可以在注释中进行类型注解,语法:# type: 类型
,示例代码如下。
num = 10 # type: int
s = "hello" # type: str
my_list = [1,2,3,4] # type: list
my_tuple = (1,2,3) # type: tuple
2.2 函数(方法)的类型注解
给函数(方法)形参类型和返回值类型进行标注,定义语法如下:
def 函数(或方法)名(形参1: 类型, 形参2: 类型, ...) -> 返回值类型:
pass
示例代码如下。函数注解的信息,保存在__annotations__
属性中可以来调用。
def sum(x: int, y: int) -> int:
return x+y
sum(1,2)
print(sum.__annotations__)
# 输出:{'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}
2.3 安装mypy
注意:类型注解仅仅是提示性的,不是决定性的,就算你类型注解错误也不会报错。像下面的代码就不会报错:变量num的类型注解为int,却把字符串"hello"赋给它了;变量s的类型注解为str,却把整数10赋给它了;函数sum的形参x,y和返回值都是int类型,却把字符串"hello"和"world"分别赋给参数x,y。
num: int = "hello"
s: str = 10
def sum(x: int, y: int) -> int:
return x+y
print(sum(1,2))
print(sum("hello ","world"))
我们可以通过mypy
库来检验最终代码是否符合设定的类型注解。mypy
是python中有关类型的静态分析工具。
#安装 mypy
pip install mypy
# 执行下面代码来查看是否符合设定的类型注解
mypy test.py
3. 标准库typing模块
上面只是简单的类型注解,传入的类型表述能力有限,不能说明复杂的类型组成情况,因此可以引入typing
模块来实现复杂的类型表达,typing
是在python 3.5 才开始有的模块。
3.1 List, Tuple, Set, Dict基础用法
(1)List[int]
表示由整数(int)组成的列表;
(2)Tuple[int, str, bool]
表示由三个对象组成的元组,其中这三个对象的类型分别为int, str, bool;
(3)Set[int]
表示由整数(int)组成的集合;
(4)Dict[int, str]
表示键是int,值是str的字典。
代码示例如下
from typing import List, Tuple, Set, Dict
my_list: List[int] = [1, 2, 3, 4, 5, 6]
my_tuple: Tuple[int, str, bool] = (1, "hi", True)
my_set: Set[int] = {1, 2, 3}
my_dict: Dict[int, str] = {1: "a", 2: "b", 3: "c"}
3.2 Union和Optional
对于更为复杂的类型注解,我们可以使用联合类型注解Union
。typing
模块中的Union
可以给一个变量或函数返回值标注多种类型,它表示或的意思。例如:data: Union[str, bytes]
,变量data可以是str
类型或bytes
类型。在Python 3.10版本及以后,推荐使用|
运算符代替Union
。示例代码如下:
from typing import Union
my_list: List[Union[int,str]] = [1,3,5,"hello,"world"]
my_dict: Dict[str, Union[str, int]]= {"name": "Jack", "age": 16}
(1)List[Union[str,int]]
:该列表中的元素可以是int类型的对象,也可以是str类型的对象;
(2)Dict[str, Union[str, int]]
:该字典中的键为str,值是str和int都可以。
typing
模块中的Optional
类型,它是Union
的一个特殊情况。如果一个变量可以是None
或其他类型,推荐使用Optional
类型,其中Optional[int]
等价于Union[int, None]
;Optional[Union[str, int, float]]
等价于Union[str, int, float, None]
。对了,Optional[]
里面只能写一个数据类型,代码示例如下
num1: Optional[Union[int, str, float]] = None
num2: Union[int, str, float,None] = None
3.3 类型别名(type alias)
因为在python中一切皆为对象,所以可将复杂一点的类型赋给某个变量,也就是起个别名,这样使用起来较为方便。
var_int = int
num1: int = 10
num2: var_int
# 以上两种类型注解的方式等价
from typing import List, Dict
list_alias = List[int]
dict_alias = Dict[str, list_alias]
my_dict: dict_alias = {"a": [1,2,3], "b": [4,5,6]}
# 以上两种类型注解的方式等价
3.4 Callable
Callable是一个可调用对象类型,语法格式为:Callable[[参数类型], 返回类型]
,包括可调用对象的参数类型和返回值类型。例如:Callable[[int], str]
,这里的int
表示可调用对象的参数类型,str
代表可调用对象的返回值类型。
from typing import Callable
def print_name(name: str) -> None:
print(f"name:{name}")
def get_name(func: Callable[[str], None]):
return func
# 利用isinstance(对象, Callable)来判断当前对象是否为可调用对象
# print(isinstance(print_name, Callable)) # 输出 True
fun = get_name(print_name)
fun("zhangsan")
3.5 TypeVar和Generic
泛型是一种可以在不指定具体类型的情况下编写可复用代码的技术。Python中的泛型是使用typing
模块中的TypeVar
和Generic
进行实现的。TypeVar
用于定义泛型类型变量,而Generic
用于定义泛型类。
(1)泛型类型变量:用于表示不确定的类型。泛型类型变量通常使用大写字母命名,比如 T、U、V。
T = TypeVar('T') # 创建一个泛型类型变量T,T可以是任意类型
(2)泛型函数:是可以接受一个或多个泛型类型参数的函数,这些参数可以用来指定函数参数的类型、返回值的类型或函数内部使用的其他类型。
from typing import TypeVar,List
T = TypeVar('T')
def print_list(my_list: List[T]) -> None:
print(my_list)
print_list([2,4,6,8]) # 可以打印整数列表
print_list(["hh","aa","www"]) # 可以打印字符串列表
(3)约束泛型:有时候我们希望泛型类型只能是特定的类型,在这种情况下,我们可以使用泛型约束来限制泛型类型的范围。
from typing import TypeVar,List
T = TypeVar('T',int,float) # 泛型类型变量T可以是int,也可以是float类型
def print_list(my_list: List[T]) -> None:
print(my_list)
print_list([2,4,6,8]) # 可以打印整数列表
print_list([1.2, 2.3, 3.4]) # 可以打印浮点数列表
(4)泛型类:是可以接受一个或多个泛型类型参数的类。这些参数可以用来指定类的属性类型、方法参数类型、方法返回值类型或类内部使用的其他类型。
from typing import TypeVar, Generic
T = TypeVar('T')
class Student(Generic[T]): # 继承Generic[T],
def __init__(self, name: T) -> None:
self.name = name
def get_name(self) -> T:
return self.name
stu = Student[str]("zhangsan") # 或者stu = Student("zhangsan")
print(stu.get_name()) # 输出:zhangsan
3.6 Any 类型
静态类型检查器将所有类型视为与Any
兼容,这意味着可对类型为Any
的值执行任何操作或方法调用,并将其赋值给任何变量。
from typing import Any
a = 10 # 变量a在默认情况下为Any类型
def print_info(var: Any) -> None:
print(var)
print_info(11)
print([1,2,3,4])
print("string")
最后再说一句:如果你按照规范的格式进行类型注解,却发现报错误,那很大可能是python版本太低。比如下面代码中的类型注解就是在python 3.10开始支持的,3.10版本以下的不支持该语法格式的类型注解。
x: int | float = 2 # 表示 or Python 3.10 开始支持