39.typing

typing

Python是一门动态语言,很多时候我们可能不清楚函数参数类型或者返回值类型,很有可能导致一些类型没有指定方法,在写完代码一段时间后回过头看代码,很可能忘记了自己写的函数需要传什么参数,返回什么类型的结果,就不得不去阅读代码的具体内容,降低了阅读的速度,typing模块可以很好的解决这个问题。

Python的typing包是从Python 3.5版本引入的标准库,它提供了类型提示和类型注解的功能,用于对代码进行静态类型检查和类型推断。typing模块中定义了多种类型和泛型,以帮助开发者代码的可读性、可维护性和可靠性。

typing的作用

typing包的主要功能如下:

  • 类型注解:typing包提供了多种用于类型注解的工具,包括基本类型(如int、str)、容器类型(如List、Dict)、函数类型(如Callable、Tuple)、泛型(如Generic、TypeVar)等。通过类型注解,可以在函数声明、变量声明和类声明中指定参数的类型、返回值的类型等,以增加代码的可读性和可靠性。
  • 类型检查:通过与第三方工具(如mypy)集成,可以对使用了类型注解的代码进行静态类型检查。类型检查可以帮助发现潜在的类型错误和逻辑错误,以提前捕获问题并改善代码的质量。
  • 泛型支持:typing模块提供了对泛型的支持,使得可以编写更通用和灵活的代码。通过泛型,可以在函数和类中引入类型参数,以处理各种类型的数据。
  • 类、函数和变量装饰器:typing模块提供了一些装饰器,如@overload、@abstractmethod、@final等,用于修饰类、函数和变量,以增加代码的可读性和可靠性。

除了typing包,还有typing_extensions模块,它是typing模块的扩展,提供了一些额外的功能和类型。typing_extensions中包括了一些高级的类型工具和类型别名,用于更复杂的类型定义和注解。

总的来说,typing包和typing_extensions模块为Python开发者提供了一套强大的类型提示工具,使得可以在代码中加入类型注解,并通过类型检查工具提供静态类型检查的功能,以提高代码的可读性和质量。

即, typing是python 3.5及以后版本的标准库,typing_extensions是typing模块的扩展包。

typing常用类型

以下是typing包中常用的类型和泛型。

注意:

  • int, float, bool, str, bytes 不需要 import typing
  • Any,Union,Tuple 等 需要 import typing
  • 基本类型:

    • int: 整数类型
    • float: 浮点数类型
    • bool: 布尔类型
    • str: 字符串类型
    • bytes: 字节类型
    • Any: 任意类型
    • Union: 多个类型的联合类型,表示可以是其中任意一个类型
    • Tuple: 固定长度的元组类型
    • List: 列表类型
    • Dict: 字典类型,用于键值对的映射
  • 泛型:

    • Generic: 泛型基类,用于创建泛型类或泛型函数
    • TypeVar: 类型变量,用于创建表示不确定类型的占位符
    • Callable: 可调用对象类型,用于表示函数类型
    • Optional: 可选类型,表示一个值可以为指定类型或None
    • Iterable: 可迭代对象类型
    • Mapping: 映射类型,用于表示键值对的映射
    • Sequence: 序列类型,用于表示有序集合类型
    • Type:泛型类,用于表示类型本身

typing使用示例

# fun1里,标明了形参和返回值的类型,fun2中却没有。
# 由于fun1里有了完整的变量类型的注释,通过help查看其使用文档就很清楚知道怎么用该函数。

from typing import List, Tuple, Dict

def fun1(a0: int, s0: str, f0: float, b0: bool) -> Tuple[List, Tuple, Dict, bool]:
    list1 = list(range(a0))
    tup1 = (a0, s0, f0, b0)
    dict1 = {s0: f0}
    b1 = b0
    return list1, tup1, dict1, b1

def fun2(a0, s0, f0, b0):
    list1 = list(range(a0))
    tup1 = (a0, s0, f0, b0)
    dict1 = {s0: f0}
    b1 = b0
    return list1, tup1, dict1, b1

print(fun1(5, "KeyName", 2.3, False))
# ([0, 1, 2, 3, 4], (5, 'KeyName', 2.3, False), {'KeyName': 2.3}, False)

print(fun2(5, "KeyName", 2.3, False))
# ([0, 1, 2, 3, 4], (5, 'KeyName', 2.3, False), {'KeyName': 2.3}, False)

print(help(fun1))
"""
Help on function fun1 in module __main__:
fun1(a0: int, s0: str, f0: float, b0: bool) -> Tuple[List, Tuple, Dict, bool]
"""

print(help(fun2))
"""
Help on function fun2 in module __main__:
fun2(a0, s0, f0, b0)
"""
([0, 1, 2, 3, 4], (5, 'KeyName', 2.3, False), {'KeyName': 2.3}, False)
([0, 1, 2, 3, 4], (5, 'KeyName', 2.3, False), {'KeyName': 2.3}, False)
Help on function fun1 in module__main__:

fun1(a0: int, s0: str, f0: float, b0: bool) -> Tuple[List, Tuple, Dict, bool]

None
Help on function fun2 in module__main__:

fun2(a0, s0, f0, b0)

None

'\nHelp on function fun2 in module__main__:\nfun2(a0, s0, f0, b0)\n'
from typing import List

def func(a: int,b: str) -> List[int or str]:# 使用or关键字表示多种类型
    list1 = []
    list1.append(a)
    list1.append(b)
    return list1

l = func(3, "aa")
print(l) # [3, 'aa']
[3, 'aa']
[3.0, 'aa']
# 定义一个不确定类型的占位符T,用作函数foo的形参, 但函数返回值一定是str,通过内部做str(name)转化来保证。
import typing

T=typing.TypeVar('T',int,float,str)

def foo(name:T)->str:
    return str(name)

print(foo(2.012)) # 2.012
print(foo(35)) # 35
print(foo("hahaha")) # hahaha
# typing中的Type是一个泛型类,用于表示类型本身,即你是啥类型就是啥类型,有点绕,看例子吧:
from typing import Type

value: Type[int]  # value是一个Type对象,表示int类型
value: int

# 即value:Type[int]和value:int是等价的。
# 那为什么还要有这个Type类呢,我理解它为一个更为宽松的类型标注,比如下面函数:
# tvm.py
def ir_module(mod: Type) -> IRModule:
    """The parsing method for ir module, by using `@ir_module` as decorator.

    Parameters
    ----------
    mod : Type
        The class to be parsed as ir module.

    Returns
    -------
    ir_module : IRModule
        The parsed ir module.
    """
    if not inspect.isclass(mod):
        raise TypeError(f"Expect a class, but got: {mod}")

    m = parse(mod, utils.inspect_class_capture(mod))
    setattr(m, "__name__", mod.__name__)
    return m

# 这个ir_module()作为一个装饰器,mod传进来是啥就是啥,但必须要是一个类型。
# 然后在函数里,通过inspect强制检查mod必须为一个类,不然就报错。我们在使用ir_module装饰器时,一般是这样的:
import tvm
from tvm.ir.module import IRModule
from tvm.script import tir as T

@tvm.script.ir_module
class MyModule:
    pass

ir_mod = MyModule
print("done")

# 即给ir_module传进去的是MyModule这个类。
# Type也可用于标注函数返回值类型,比如
from typing import Type, List

def get_list_type() -> Type[List[int]]:
    return list

returned_type = get_list_type()  # returned_type是一个Type对象,表示List[int]类型

# 在上述示例中,Type[List[int]]表示List[int]类型本身,可以用于注解函数get_list_type的返回类型,说明该函数返回的是List[int]类型的值。
# 通过调用get_list_type()函数得到的返回值returned_type是一个Type对象,表示List[int]类型。

# 或标注函数参数类型:
from typing import Type

def process_type(t: Type[str]) -> None:
    print(t)

process_type(str)  # 输出: <class 'str'>
# 在上述示例中,Type[str]用于注解函数process_type的参数类型,说明函数接受一个类型为str的参数。
# 通过在调用process_type函数时传入str作为参数,可以在函数内部获取到该类型的Type对象。