注解:
Python 运行时不强制执行函数和变量类型注解,但这些注解可用于类型检查器、IDE、静态检查器等第三方工具。
在定义函数时使用“注解”的形式来标注形参和返回值的类型,但这种注解的形式并不会对形参进行任何约束和检查,在实际调用函数时,即使实参不符合形参的类型标注,一样能够正常传递。
Python 中几种基本的变量类型都得到了支持:
a: int = 3
b: float = 3.14
c: str = 'abc'
d: bool = False
下面的函数接收与返回的都是字符串,注解方式如下:
示例代码1:
"""
greeting 函数中,参数 name 的类型是 str,返回类型也是 str。子类型也可以当作参数。
"""
def greeting(name: str) -> str:
return 'Hello ' + name
a = greeting('dgw')
print(a)
运行结果:
类型别名:
把类型赋给别名,就可以定义类型别名。本例中,Vector
和 list()
相同,可互换:
示例代码2:
Vector = list()
def scale(scalar: float, vector: Vector) -> Vector:
return [scalar * num for num in vector]
# typechecks; a list of floats qualifies as a Vector.
new_vector = scale(2.0, [1.0, -4.2, 5.4])
print(new_vector)
运行结果:
示例代码3:
from typing import Tuple
# 类型别名 --->给类型赋予一个有意义的名称
Vector2D = Tuple[int, int] # Vector2D 这个名称清晰的表达了这个对象是一个二维的向量。
def foo(vector: Vector2D):
print(vector)
foo(vector=(1, 2))
foo((1, 2))
运行结果:
示例代码4:【与类型别名有点类似的,是用 NewType
创建自定义类型,但不同的是 NewType
创建了原始类型的“子类”】
from typing import Tuple
from typing import NewType
# 类型别名 --->给类型赋予一个有意义的名称
Vector2D = Tuple[int, int] # Vector2D 这个名称清晰的表达了这个对象是一个二维的向量。
def foo(vector: Vector2D):
print(vector)
# 创建新类型
Vector3D = NewType('Vector3D', Tuple[int, int, int])
def bar(vector: Vector3D):
print(vector)
# 类型检查成功
# 类型别名和原始类型是等价的
foo(vector=(1, 2))
# 类型检查失败
# NewType创建的是原始类型的“子类”
bar(vector=(1, 2, 3))
# 类型检查成功
# 传入参数必须是 Vector3D 的“实例”
v_3d = Vector3D((4, 5, 6))
bar(vector=v_3d)
运行结果:
示例代码5:
def demo(name: str = '名字', age: int = 31) -> "str":
print("函数注解", demo.__annotations__) # 查看函数注解信息
print("打印实参", name, age)
print(type(name), type(age))
ret = "name:" + name + ', age:' + str(age)
return ret
a = demo()
print(a)
print('*' * 100)
b = demo('张三')
print(b)
print('*' * 100)
c = demo('张三', 25)
print(c)
print('*' * 100)
print("函数注解", demo.__annotations__) # 查看函数注解信息
运行结果:
容器类型:
列表、字典、元组等包含元素的复合类型,用简单的 list,dict,tuple 不能够明确说明内部元素的具体类型。
因此要用到 typing
模块提供的复合注解功能:
示例代码6:
from typing import List, Dict, Tuple
# 参数1: 元素为 int 的列表
# 参数2: 键为字符串,值为 int 的字典
# 返回值: 包含两个元素的元组
def mix(scores: List[int], ages: Dict[str, int]) -> Tuple[int, int]:
print(scores)
print(ages)
return (0, 0)
a = mix(99, ('zhangsan', 25))
print(a)
运行结果:
如果是 Python 3.9+ 版本,甚至连 typing
模块都不需要了,内置的容器类型就支持了复合注解:
def mix(scores: list[int], ages: dict[str, int]) -> tuple[int, int]:
return (0, 0)
在某些情况下,不需要严格区分参数到底是列表还是元组(这种情况还蛮多的)。这时候就可以将它们的特征抽象为更泛化的类型(泛型),比如 Sequence(序列)。
示例代码7:
# Python 3.8 之前的版本
from typing import Sequence as Seq1
def foo(seq: Seq1[str]):
for item in seq:
print(item)
foo([1, 2, 3])
# Python 3.9+ 也可以这么写
from collections.abc import Sequence as Seq2
def bar(seq: Seq2[str]):
for item in seq:
print(item)
bar([11, 22, 33])
上述代码中函数的参数不对容器的类型做具体要求,只要它是个序列(比如列表和元组)就可以。
Any:
如果实在不知道某个类型注解应该怎么写时,这里还有个最后的逃生通道:
示例代码8:
from typing import Any
def foo() -> Any:
pass
任何类型都与 Any
兼容。当然如果你把所有的类型都注解为 Any
将毫无意义,因此 Any
应当尽量少使用。
参考链接:typing —— 类型注解支持 — Python 3.10.5 文档
参考文章:Python类型注解,你需要知道的都在这里了 - 知乎