前言
typing 是在 python 3.5 才有的模块
类型提示
python是一种动态语言,即在运行时不用指定数据类型。
没使用类型提示时
需要使用注释才可以知道类型
# 'l1' is a list of integers
l1 = [] # type: List[int]
# 'str1' is a string (Note: initial value is a problem)
str1 = ... # type: str
class Starship:
# 'stats' is a class variable
stats = {} # type: Dict[str, int]
当使用了类型提示
from typing import List, ClassVar, Dict
# int 变量,默认值为 0
num: int = 0
# bool 变量,默认值为 True
bool_var: bool = True
# 字典变量,默认为空
dict_var: Dict = {}
# 列表变量,且列表元素为 int
primes: List[int] = []
class Starship:
# 类变量,字典类型,键-字符串,值-整型
stats: ClassVar[Dict[str, int]] = {}
# 实例变量,标注了是一个整型
num: int
此时的类型提示更像是一个约束规范,但并不是语法,也就是当你传错类型的话,它也不会报错。
变量类型提示,元组打包
# 正常的元组打包
a = 1, 2, 3
# 加上类型提示的元组打包
t: Tuple[int, ...] = (1, 2, 3)
print(t)
t = 1, 2, 3
print(t)
# py3.8+ 才有的写法
t: Tuple[int, ...] = 1, 2, 3
print(t)
t = 1, 2, 3
print(t)
# 输出结果
(1, 2, 3)
(1, 2, 3)
(1, 2, 3)
(1, 2, 3)
在这里要注意省略号的使用,不写会出现警告,但不会报错
变量类型提示-元组解包
# 正常元组解包
message = (1, 2, 3)
a, b, c = message
print(a, b, c) # 输出 1 2 3
# 加上类型提示的元组解包
header: str
kind: int
body: Optional[List[str]]
# 不会 warning 的栗子
header, kind, body = ("str", 123, ["1", "2", "3"])
# 会提示 warning 的栗子
header, kind, body = (123, 123, ["1", "2", "3"])
!在这里提到了Optional
在类里使用类型提示
class BasicStarship:
captain: str = 'Picard' # 实例变量,有默认值
damage: int # 实例变量,没有默认值
stats: ClassVar[Dict[str, int]] = {} # 类变量,有默认值
ClassVar
是 typing 模块的一个特殊类
它向静态类型检查器指示不应在类实例上设置此变量
3.常用类型提示
int,long,float: 整型,长整形,浮点型;
bool,str: 布尔型,字符串类型;
List, Tuple, Dict, Set:列表,元组,字典, 集合;
Iterable,Iterator:可迭代类型,迭代器类型;
Generator:生成器类型;
前两行小写的不需要 import,后面三行都需要通过 typing 模块 import 哦
举个简单的例子
from typing import Tuple, List, Dict
# 返回一个 Tuple 类型的数据,第一个元素是 List,第二个元素是 Tuple,第三个元素是 Dict,第四个元素可以是字符串或布尔
def add(a: int, string: str, f: float, b: bool or str) -> Tuple[List, Tuple, Dict, str or bool]:
list1 = list(range(a))
tup = (string, string, string)
d = {"a": f}
bl = b
return list1, tup, d, bl
# 不 warn 的调用写法
print(add(1, "2", 123, True))
# 输出结果
([0], ('2', '2', '2'), {'a': 123}, True)
4.NewType
可以自定义创一个新类型
主要用于类型检查
NewType(name, tp) 返回一个函数,这个函数返回其原本的值
静态类型检查器会将新类型看作是原始类型的一个子类
tp 就是原始类型
5.Callable
是一个可调用对象类型,查看对象是否可调用
语法:
# 返回True或False
isinstance(对象, Callable)
6.Any Type
一种特殊的类型是 Any
静态类型检查器会将每种类型都视为与 Any 兼容,将 Any 视为与每种类型兼容
# Any
from typing import Any
a = None # type: Any
a1 = [] # OK
a2 = 2 # OK
s = '' # type: str
s1 = a # OK
def foo(item: Any) -> int:
# Typechecks; 'item' 可以是任意类型
print(item)
return 1
foo(a)
foo(a1)
foo(a2)
foo(s)
foo(s1)
7.Union(重要)
联合类型
Union[int, str] 表示既可以是 int,也可以是 str
vars: Union[int, str]
# 等价于
vars: [int or str]
vars: Union[int]
# 等价于
vars: int
union 等价写法
Union[int] == int
最终 Union[int] 返回的也是 int 类型
Union[int, str, int] == Union[int, str]
重复的类型参数会自动忽略掉
Union[int, str] == Union[str, int]
自动忽略类型参数顺序
Union[Union[int, str], float] == Union[int, str, float]
union 嵌套 union 会自动解包
8.Optional(在leetcode看到该写法)
可选类型
和默认参数有什么不一样
官方原话:可选参数具有默认值,具有默认值的可选参数不需要在其类型批注上使用 Optional,因为它是可选的
不过 Optional 和默认参数其实没啥实质上的区别,只是写法不同
使用 Optional 是为了让 IDE 识别到该参数有一个类型提示,可以传指定的类型和 None,且参数是可选非必传的
# 可选参数
def foo(arg: int = 0) -> None:
...
# 不传 arg 默认取 0
foo()
重点
Optional[int] 等价于 Union[int, None]
意味着:既可以传指定的类型 int,也可以传 None
Optional[] 里面只能写一个数据类型
# 正确
Optional[str]
Optional[List[str]]
Optional[Dict[str, Any]]
# 错误
Optional[str, int]
Optional[Union[str, int, float]]