Python中typing模块的类型注解 基于typing的具名元组定义方法
一、python的注解
Python 是一种动态语言,在声明一个变量时我们不需要显式地声明它的类型,在给函数传值时我们是不知道参数到底应该传入什么类型的。
这样其实就造成了很多不方便的地方,在某些情况下一些复杂的方法,如果不借助于一些额外的说明,我们是不知道参数到底是什么类型的。
因此,Python 中的类型注解就显得比较重要了。
在 Python 3.5 中,Python PEP 484 引入了类型注解(type hints),
在 Python 3.6 中,PEP 526 又进一步引入了变量注解(Variable Annotations),我们可以这样对函数进行注解
类型和变量注解都只是提供了一种提示,对于运行实际上没有任何影响,你随便传也没关系,不按照注解返回也一点问题没有
不过有了类型注解,一些 IDE 是可以识别出来并提示的,比如 PyCharm 就可以识别出来在调用某个方法的时候参数类型不一致,会飘黄,提示 WARNING
具体的语法是可以归纳为两点:
- 在声明变量时,变量的后面可以加一个冒号,后面再写上变量的类型,如 int、list 等等。
- 在声明方法返回值的时候,可以在方法的后面加一个箭头,后面加上返回值的类型,箭头左右两边都要留有空格,如 int、list 等等。
注解举例:
# python的注解 names: list = ['Germey', 'Guido'] version: tuple = (3, 7, 4) operations: dict = {'show': False, 'sort': True}
二、typing模块
上面的注解看上去没毛病,但是对于list内部到底是什么类型就没办法知道了,所以需要一种更强类型的注解,可以借助 typing 模块
目前 typing 模块也已经被加入到 Python 标准库中,不需要安装第三方模块,我们直接导入就可以直接使用了
from typing import List, Tuple, Dict, Set, Union, Callable, Literal, Iterable, NoReturn list1: List[str] = ['Germey', 'Guido'] list2: List[int or float] = [2, 3.5] list3: List[List[int]] = [[1, 2], [2, 3]] # 支持嵌套 tuple1: Tuple[int, int, int] = (3, 7, 4) tuple2: Tuple[str, int, float] = ('Mike', 22, 1.75) dict1: Dict[str, bool] = {'show': False, 'sort': True} # Union,联合类型,Union[X, Y] 代表要么是 X 类型,要么是 Y 类型。 def process(func: Union[str, Callable]): if isinstance(func, str): pass elif isinstance(func, Callable): func() # Literal,自定义可选参数,如下表明msg_type变量可选项 msg_type: Literal['text', 'pic', 'file', 'link', 'code','applet','video_account'] # Iterable,可迭代的,如元组、列表,如下#表明返回值是列表,第一个元素是多个UserMessage组成的列表或元组 UserMessage = Tuple[str, str] all_boy = Literal[True, False] def get_msgs(chat: str='张三', user_type: int=2)-> [Iterable[UserMessage], all_boy, str, int]: msgs_list = [('张三','你好'), ('李四','hello'), ('王麻子', '哈哈哈')] return [msgs_list , True, '2018-03-03', 3] # Callable,可调用类型,它通常用来注解一个方法,并且要指明方法的参数和返回值 def date(year: int, month: int, day: int) -> str: return f'{year}-{month}-{day}' def get_date_fn() -> Callable[[int, int, int], str]: return date # NoReturn,没有返回结果 def hello() -> NoReturn: print('hello')
有了这样的声明,以后我们如果看到方法的定义,我们就知道传入的参数类型了,如调用 add 方法的时候,我们就知道传入的需要是一个数值类型的变量,而不是字符串类型,非常直观。
但值得注意的是,这种类型和变量注解实际上只是一种类型提示,对运行实际上是没有影响的
三、typing模块的具名元组
typing 模块里面的具名元组本质上还是调用collections.namedtuple,个人感觉typing好用一点:
1.collections.namedtuple实现具名元组
from collections import namedtuple # collections的定义方式 Sender = namedtuple('Sender', ['name', 'type', 'company']) TypedMessage = namedtuple('TypedMessage', ['type', 'value', 'link_info']) UserMessage = namedtuple('UserMessage', ['name', 'message', 'company'])
2.typing.NameTuple 实现具名元组
from typing import NamedTuple, Literal # typing的定义方式,本质还是collections的,更清晰,一下就能看出是什么 class Sender(NamedTuple): name: str type: Literal['user', 'self'] company: str class TypedMessage(NamedTuple): type: Literal['text', 'pic', 'file', 'link', 'code','applet','video_account'] # value: str link_info: list = ['', '', '']# 链接卡片的头部文字、描述文字、图标地址,默认为空 class UserMessage(NamedTuple): name: str message: TypedMessage # 自己定义的具名元组类型 company: str if __name__ == '__main__': sender = Sender('小明', 'user', '百度') type_message = TypedMessage('text', '早上好') user_message = UserMessage(name=sender.name, message=type_message, company=sender.company) print(sender.name, sender.type, sender.company) # 小明 user 百度 print(type_message.type, type_message.value, type_message.link_info) # text 早上好 ['', '', ''] print(user_message.name, user_message.message==type_message, user_message.company) # 小明 True 百度