在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)像静态语言那样分析我们的代码,及时给我们相应的提示,如下图对比:
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 = b'world'
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)