前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python3 Typing模块详解

Python3 Typing模块详解

原创
作者头像
windealli
发布2023-11-17 10:36:05
9030
发布2023-11-17 10:36:05
举报
文章被收录于专栏:windealli

关注公众号查看作者更多文章,公众号:海天二路搬砖工

一、引言

在 Python 3 之前,Python 是一种弱类型语言,类型是不显式地声明的,Python 可以在运行时根据上下文自动推断出变量或参数的类型。这一特性常常导致程序运行时因类型不匹配而引发一系列异常,给程序员带来了很大的困扰。

为此,Python3中引入了静态类型注解(Type hints),用于在 Python 代码中显式地注明变量、函数参数和函数返回值的类型。typing 模块是为 Python 提供静态类型注解的一组工具,它使 Python 开发者能够清晰明了地注释变量、方法和函数的数据类型。

二、Typing模块简介

typing 模块是 Python 3 中新增加的模块,它是一组静态类型注解工具。

typing 模块的作用

  1. 类型注释: typing模块提供了大量用于类型注释的工具,并使开发人员能够使用自己自定义数据类型。通过将这些类型注释添加到变量、函数、类、方法、属性等数据结构中,可以更清晰、易于理解地表示数据类型,有助于其他人更好地理解代码。
  2. 类型检查: 因为 Python 是一门动态类型语言,导致类型错误容易在运行时发生。typing 模块允许静态类型检查,这是在运行之前在代码中检测类型错误的过程,这可帮助程序员尽早发现和修复任何类型相关的错误,并提高代码质量。
  3. 数据容器: typing模块中提供了非常强大的数据容器类型,如ListTupleDictSet 等,帮助开发人员更有效地操作数据结构。通过使用typing,我们可以使用Python语言提供的丰富数据结构,并用更准确的方式进行标注和注释。
  4. 泛型编程支持: typing提供了定义泛型类和函数的快捷方式,提供了思考和解决泛型编程的强有力的支持。
  5. 类型别名: typing模块允许开发人员创建自己的类型别名,以提高代码的可读性和可维护性。这可让类型字典更简洁,同时确保它们与代码实际使用的类型一致。
  6. 增强可读性: 通过使用typing模块提供的类型注释,我们可以使代码更具可读性和可理解性,有助于开发人员能够更好地阅读和理解代码。

三、基本类型注释

最基本的类型注释(intstrbool等)

typing模块定义了一些最基本的数据类型别名,如 intfloatstrbool 等。它可以与参数、变量和函数返回值一起使用。

代码语言:javascript
复制
def greeting(name: str) -> str:
    """
    接收 str 类型参数 name,返回 str 类型。
    """
    return 'Hello ' + name

在这个示例中,参数 name 被注释为 str 类型,返回类型也被注释为 str 类型。

Union类型注释

Union 类型允许同时使用多个数据类型,其中任何一种类型的值都可以传递给函数。在注释中,我们使用 or 或 | 分隔多个数据类型。

代码语言:javascript
复制
from typing import Union

def get_user_input() -> Union[str, int]:
    """
    该函数返回 str 或 int 数据类型结果。
    """
    user_input = input("Please enter an integer or string: ")
    if user_input.isdigit():
        return int(user_input)
    else:
        return user_input

Optional 类型注释

Optional类型表示一个可选的数据类型,它可用于表示参数可以是一种数据类型或 None 值。我们使用 Optional[type] 表示该函数参数可以是 typeNone 值。

代码语言:javascript
复制
from typing import Optional

def get_username() -> Optional[str]:
    """
    当获取到正确的用户名时,返回 str 类型数据;否则返回 None。
    """
    try:
        username = input("Please enter your username: ")
        if username:
            return str(username)
    except:
        return None

在这个示例中,我们使用 Optional[str] 来表示 get_username() 函数返回 str 类型数据或 None 值。

四、容器类类型注释

简单的容器类型注释

typing模块对常用的容器类型提供了类型注释关键字ListTupleDictSet

  • List 类型注释: List 的类型注释中使用方括号来指定列表中每个元素的类型。
  • Tuple 类型注释: Tuple 类型注释可以使用方括号表示Tuple中每个元素数据类型。我们也可以使用...允许将数量未知的元素包括在 Tuple 数据类型之中。
  • Dict 类型注释: Dict 类型注释使用键和值的数据类型注释来描述一个字典的键和值。
  • **Set 类型注释**: Set 类型注释可以用花括号和 Set 关键字来表示,用于指定集合中元素的类型。

**List 示例:**

代码语言:javascript
复制
from typing import List

def sort_numbers(numbers: List[int]) -> List[int]:
    """
    接收一个整数列表,并返回按照升序排列后的列表。
    """
    return sorted(numbers)

在这个示例中,我们使用 List[int] 将 numbers 参数注释为整数类型列表。

Dict 示例:

代码语言:javascript
复制
from typing import Dict

def user_info(name: str, age: int) -> Dict[str, Union[str, int]]:
    """
    接受用户名和年龄,返回一个包含用户名和年龄的字典。
    """
    user = {}
    user['name'] = name
    user['age'] = age
    return user

在这个示例中,我们使用 Dict[str,Union[str, int]] 将函数的返回类型注释为一个字典,包含字符串类型的键和字符串或整型的值。

嵌套的容器类型

嵌套容器(Nested container)是指容器中又包含其他容器。 typing 模块为这种数据类型提供了更复杂的注释方式。

代码语言:javascript
复制
from typing import List, Tuple

def my_function(arg1: List[Tuple[int, str]]) -> List[str]:
    """
    接受一个整型列表中包含元组(整型,字符串),返回由元组中包含的字符串组成的列表。
    """
    return [x[1] for x in arg1]

在这个示例中,参数 arg1 被注释为一个 List,每个元素都是一个 Tuple,其中第一个元素是 int 类型,第二个元素是 str 类型。函数的返回类型为 List[str] 数据类型,一个字符串类型的列表。

五、 Callable 类型注释

**Callable**类型注释和它们的变体

typing 模块提供了可以用来注释函数类型的 Callable 类型,包括 Callable[[args], return_type] 以及 Callable[..., return_type]

(1)Callable[[args], return_type]的注释使用方括号, args 表示函数参数的类型, return_type 表示函数返回值的类型。

代码语言:javascript
复制
from typing import Callable

def repeat(word: str, times: int, callback: Callable[[str, int], str]) -> None:
    """
    接收字符串word、重复次数times和一个回调函数,它会使用回调函数重复一个词给定的次数。
    """
    for i in range(times):
        result = callback(word, i)
        print(result)

(2) Callable[..., return_type] 的注释使用省略符、返回值类型表示被调用的函数的返回值类型。省略号表示这个函数任意数量和类型的参数,也就是说这个函数类型可以接受任意数量和类型的参数。

代码语言:javascript
复制
from typing import Callable

def logger(func: Callable[..., str]) -> Callable[..., str]:
    def wrapped(*args, **kwargs) -> str:
        result = func(*args, **kwargs)
        print("Got result: ", result)
        return result
    return wrapped

在这个示例中,使用 Callable[..., str] 将一个函数类型的 func 参数注释为一个可调用的函数类型,它的返回值是 str 类型,同时使用 *args 和 **kwargs 参数传递任意数量和类型的参数。

使用**Callable**类型来给函数参数和返回值加上类型注释

可以将 Callable 类型注释用于函数的参数和返回值,

代码语言:javascript
复制
from typing import Callable

def apply_operation(func: Callable[[int, int], int], a: int, b: int) -> int:
    """
    接受两个整型变量 a 和 b 以及一个将整型变量映射到整型变量的函数func,返回这个函数的执行结果。
    """
    return func(a, b)

def add(a: int, b: int) -> int:
    """
    返回 a + b 的和
    """
    return a + b

def multi(a: int, b: int) -> int:
    """
    返回 a * b 的积
    """
    return a * b

result_add = apply_operation(add, 4, 5)
result_multi = apply_operation(multi, 4, 5)

六、类型别名(Type Aliases)

什么是类型别名

类型别名是指我们可以使用一个给定的名称将一个数据类型表示成等效的形式,这种方式可以简化代码的阅读和理解。typing 模块提供了一个 TypeAlias 类型,用于给数据类型定义别名。

代码语言:javascript
复制
from typing import List, TypeAlias

Vector = List[float]

def scale(scalar: float, vector: Vector) -> Vector:
    """
    接受一个浮点类型的倍数 scalar 和一个 float 类型的 Vector 列表,返回一个缩放后的 Vector。
    """
    return [scalar * num for num in vector]

new_vector = scale(2.0, [1.0, -4.2, 5.4])

这个示例中,我们定义了一个名为 Vector 的类型别名,它表示一个 float 类型的列表。函数 scale() 就使用了这个别名。

使用类型别名来简化类型注释

类型别名可以用于把复杂的类型注释简单化:

代码语言:javascript
复制
from typing import Dict, List, Union

UserInfo = Dict[str, Union[str, int]]
AddressBook = List[UserInfo]

def showbook(book: AddressBook) -> None:
    """
    接收一个 AddressBook 类型的参数并打印其中的用户信息
    """
    for user in book:
        print(f"Name: {user['name']}")
        print(f"Age: {user['age']}")
        print(f"Address: {user['address']}")

def add_user(book: AddressBook, name: str, age: int, address: str) -> None:
    """
    将一名新用户添加到 AddressBook 中,并写入磁盘。
    """
    new_user = UserInfo(name=name, age=age, address=address)
    book.append(new_user)
    write_to_disk(book)

def write_to_disk(book: AddressBook) -> None:
    """
    将整个 AddressBook 列表写入磁盘文件。
    """
    # 实现将 book 写入磁盘代码
    pass

在这个示例中,我们使用类型别名的方式来简化代码的类型注释,用 UserInfo 表示一个字典类型,表示的是一个用户信息; 用 AddressBook 表示一个列表类型,其中每个元素都是一个 UserInfo 类型的字典。

创建自定义类型别名

我们可以使用 TypeAlias 的类型别名来定义自己的数据类型。一个类型别名可以用于多个数据类型的定义.

代码语言:javascript
复制
from typing import TypeAlias, Union

Number = Union[int, float, complex]
Matrix = List[List[Number]]

def add_matrices(a: Matrix, b: Matrix) -> Matrix:
    """
    接收两个 Number 类型列表,并返回其劈叉后的 Matrix 类型。
    """
    if len(a) != len(b) or len(a[0]) != len(b[0]):
        raise ValueError("Matrices have different size")
    res = []
    for i in range(len(a)):
        res.append([])
        for j in range(len(a[0])):
            res[i].append(a[i][j] + b[i][j])
    return res

在这个示例中,我们定义了 Number 类型别名表示 float、int、complex 三种数据类型中的任意一种。同时,我们使用 Matrix 类型别名表示二维数组,其中的元素可以是 Number 类型别名中定义的任意一种类型。

七、typing与泛型编程

什么是泛型编程

泛型编程是一种编程方法,它允许你写出更加通用、灵活、可复用的代码。使用泛型编程,可以定义函数和类,并允许它们在运行时接受不同类型的参数和数据结构。

在 Python 中,我们可以使用 typing 模块来实现泛型编程。typing 模块提供了大量的类型,包括数字、列表、字典、元组等常见的数据类型,并且使用泛型类型注释来描述容器类型中的元素类型和数量。

泛型函数与泛型类

泛型类型注释允许我们定义泛型函数和泛型类,可以将这些类型注释用于函数的参数、返回值和函数本身。泛型类型注释用于泛型函数和泛型类时,这些函数和类将接受不同类型的参数,并在运行时创建新的类型。

泛型函数:

代码语言:javascript
复制
from typing import TypeVar

T = TypeVar('T')

def reverse_list(lst: List[T]) -> List[T]:
    """
    返回一个反转后的列表
    """
    return lst[::-1]

在这个函数中,我们使用了泛型类型变量 T 来表示列表里的元素类型,这样就可以处理任何类型的列表。List[T] 表示一个元素为 T 类型的列表,因此 reverse_list 函数可以应用于任何类型的列表。

泛型类:

代码语言:javascript
复制
from typing import TypeVar, Generic

T = TypeVar('T')

class Queue(Generic[T]):
    """
    一个泛型队列类
    """
    def __init__(self):
        self.items = []

    def enqueue(self, item: T) -> None:
        """
        将元素添加到队列的末尾
        """
        self.items.append(item)

    def dequeue(self) -> T:
        """
        从队列的前面删除并返回第一个元素
        """
        return self.items.pop(0)

在这个泛型队列类的定义中,我们使用了泛型类型变量 T 来表示队列中元素的类型。在 enqueue 方法中,我们可以添加任何类型的元素到队列中,而在 dequeue 方法中,返回队列中的第一个元素,这个元素的类型就是 T。这样,我们定义的 Queue 类就可以处理任何类型的元素。

泛型类型变量和泛型约束

泛型类型变量是一种用于在泛型类型注释中定义类型参数的方式。使用泛型类型变量,可以定义一个函数可以接受一个不确定的类型,但这个类型需要满足一些特定的限制。这些限制被称为泛型约束。

代码语言:javascript
复制
from typing import TypeVar

Number = TypeVar('Number', int, float, complex)

def add(a: Number, b: Number) -> Number:
    return a + b

在这个示例中,我们使用 TypeVar 定义了一个名为 Number 的泛型类型变量,它可以接受 intfloatcomplex 类型。 我们使用 Number 泛型类型变量来定义了 ab 的类型,因此这个函数可以接受 intfloatcomplex 类型的参数。

九、总结

Python3 Typing 模块作为 Python 语言中类型提示的标准方法,提供了许多元编程工具,为 Python 开发者提供了更好的类型检查和静态分析工具。使用 typing 模块,Python 开发者可以更轻松地声明函数和类的类型,并实现更好的类型推断和错误检查。

十、我的公众号

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 关注公众号查看作者更多文章,公众号:海天二路搬砖工
  • 一、引言
  • 二、Typing模块简介
    • typing 模块的作用
    • 三、基本类型注释
      • 最基本的类型注释(int、str、bool等)
        • Union类型注释
          • Optional 类型注释
          • 四、容器类类型注释
            • 简单的容器类型注释
              • 嵌套的容器类型
              • 五、 Callable 类型注释
                • **Callable**类型注释和它们的变体
                  • 使用**Callable**类型来给函数参数和返回值加上类型注释
                  • 六、类型别名(Type Aliases)
                    • 什么是类型别名
                      • 使用类型别名来简化类型注释
                        • 创建自定义类型别名
                        • 七、typing与泛型编程
                          • 什么是泛型编程
                            • 泛型函数与泛型类
                              • 泛型类型变量和泛型约束
                              • 九、总结
                              • 十、我的公众号
                              相关产品与服务
                              容器服务
                              腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                              领券
                              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档