转自 Python -类型提示 Type Hints - 小菠萝测试笔记 - 博客园 (cnblogs.com)
为什么会有类型提示
- Python是一种
动态类型
语言,这意味着我们在编写代码的时候更为自由,运行时不需要指定
变量类型 - 但是与此同时 IDE 无法像静态类型语言那样分析代码,及时给我们相应的提示,比如字符串的 split 方法
def split_str(s):
strs = s.split(",")
由于不知道参数 s 是什么类型,所以当你敲 s.
的时候不会出现 split 的语法提示
常用类型提示
typing 是在 python 3.5 才有的模块
常用类型提示
int,long,float
: 整型,长整形,浮点型;bool,str
: 布尔型,字符串类型;List, Tuple, Dict, Set
:列表,元组,字典, 集合;Iterable,Iterator
:可迭代类型,迭代器类型;Generator
:生成器类型;
前两行小写的不需要 import,后面三行都需要通过 typing 模块 import 哦
例子
指定函数参数类型
单个参数
# name 参数类型为 str
def greeting(name: str) :
return "hello"
多个参数
# 多个参数,参数类型均不同
def add(a: int, string: str, f: float, b: bool or str):
print(a, string, f, b)
bool or str
:代表参数 b 可以是布尔类型,也可以是字符串
指定函数返回的参数类型
# 函数返回值指定为字符串
def greeting(name: str) -> str:
return "hello"
复杂一点的栗子
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)
那指定类型的时候用 list、set、dict、tuple 可不可以呢?
可以是可以,但是不能指定里面元素数据类型
def test(a: list, b: dict, c: set, d: tuple):
print(a, b, c, d)
List[T]、Set[T]
只能传一个类型,传多个会报错
a: List[int, str] = [12,"34"]
b: Set[int, str] = {12,"23"}
print(a)
print(b)
换成
a: List[int] = [12,"34"]
b: Set[int] = {12,"23"}
print(a)
print(b)
Dict[T,T]
e: Dict[str, int] = {"as": 123, "643": 34}
print(e)
Tuple[T]
可以传入多个
d: Tuple[int, str] = (1, "2")
print(d)
# 输出结果
(1, '2')
只写一个int,但是赋值两个int
from typing import Tuple, List, Dict, Set
d: Tuple[int, str] = (1)
print(d)
只写一个 int,赋值两个 int 元素会报 warning
写了两个int,但是赋值多于两个
d: Tuple[int, str] = (1, "2", "2")
不会报错,但是也会有 warning
指定一个类型,对所有元素生效
d: Tuple[int, ...] = (1, 2, 3)
d: Tuple[Dict[str, str], ...] = ({"name": "poloyy"}, {"age": "33"})
类型别名
可以将复杂一点类型给个别名,这样好用一些
变量例子
# 别名
vector = List[float]
var: vector = [1.1, 2.2]
# 等价写法
var: List[float] = [1.1, 2.2]
函数例子
# float 组成的列表别名
vector_list_es = List[float]
# 字典别名
vector_dict = Dict[str, vector_list_es]
# 字典组成列表别名
vector_list = List[vector_dict]
# vector_list 等价写法,不用别名的话,有点像套娃
vector = List[Dict[str, List[float]]]
# 函数
def scale(scalar: float, vector: vector_list) -> vector_list:
for item in vector:
for key, value in item.items():
item[key] = [scalar * num for num in value]
print(vector)
return vector
scale(2.2, [{"a": [1, 2, 3]}, {"b": [4, 5, 6]}])
# 输出结果
[{'a': [2.2, 4.4, 6.6000000000000005]}, {'b': [8.8, 11.0, 13.200000000000001]}]
NewType
可以自定义创一个新类型
- 主要用于类型检查
- NewType(name, tp) 返回一个函数,这个函数返回其原本的值
- 静态类型检查器会将新类型看作是原始类型的一个子类
- tp 就是原始类型
from typing import NewType
UserId = NewType('UserId123', int)
def name_by_id(user_id: UserId) -> str:
print(user_id)
return str(user_id)
UserId('user') # Fails type check
num = UserId(5) # type: int
name_by_id(42) # Fails type check
name_by_id(UserId(42)) # OK
print(type(UserId(5)))
可以看到 UserId 其实也是 int 类型
Callable
是一个可调用对象类型
查看对象是否可调用
# 返回True或False
isinstance(对象, Callable)
例子
# 最简单的函数
def print_name(name: str):
print(name)
# 判断函数是否可调用
print(isinstance(print_name, Callable))
x = 1
print(isinstance(x, Callable))
# 输出结果
True
False
函数是可调用的,所以是 True,而变量不是可调用对象,所以是 False
Callable 作为函数返回值
# Callable 作为函数返回值使用,其实只是做一个类型检查的作用,看看返回值是否为可调用对象
from typing import NewType,Callable
def print_name(name: str):
print(name)
def get_name_return() -> Callable[[str], None]:
return print_name
vars = get_name_return()
vars("test")
# 等价写法,相当于直接返回一个函数对象
def get_name_test():
return print_name
vars2 = get_name_test()
vars2("test")
TypeVar 泛型
任意类型
# 可以是任意类型
T = TypeVar('T')
def test(name: T) -> T:
print(name)
return name
test(11)
test("aa")
# 输出结果
11
aa
指定类型
# 可以是 int,也可以是 str 类型
AA = TypeVar('AA', int, str)
num1: AA = 1
num2: AA = "123"
print(num1, num2)
num3: AA = []
# 输出结果
1 123
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)
隐式使用 Any
def legacy_parser(text):
...
return data
# 上述写法等价于下述写法
# 所有没有返回类型或参数类型的函数将隐式默认使用 Any
def legacy_parser(text: Any) -> Any:
...
return data
Union
联合类型
Union[int, str] 表示既可以是 int,也可以是 str
from typing import Union
# vars 变量可以是int也可以是 str 类型
vars: Union[int,str]
vars = 1
print(vars)
vars = "123"
print(vars)
# 赋值列表会有warning
vars = []
print(vars)
等价写法
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 会自动解包
Optional
可选类型
和默认参数有什么不一样
- 官方原话:可选参数具有默认值,具有默认值的可选参数不需要在其类型批注上使用 Optional,因为它是可选的
- 不过 Optional 和默认参数其实没啥实质上的区别,只是写法不同
- 使用 Optional 是为了让 IDE 识别到该参数有一个类型提示,可以传指定的类型和 None,且参数是可选非必传的
# 可选参数
def foo(arg: int = 0) -> None:
...
# 不传 arg 默认取 0
foo()
Optional[int]
等价于Union[int, None]
- 意味着:既可以传指定的类型 int,也可以传 None
例子
使用 Optional
def foo_func(arg: Optional[int] = None):
print(arg)
foo_func()
foo_func(1)
# 输出结果
None
1
使用默认参数的写法
def foo_func(arg: int = None):
print(arg)
foo_func()
foo_func(1)
# 输出结果
None
1
这种写法,Pycharm 并不会 warning
重点
Optional[] 里面只能写一个数据类型
# 正确
Optional[str]
Optional[List[str]]
Optional[Dict[str, Any]]
# 错误
Optional[str, int]
Optional[Union[str, int, float]]