深入Python typing模块:掌握Optional、Annotated等,写出更优雅的代码

内容来源公众号:胜天半月子

学习typing模块

在学习 typing 模块及其扩展时,我们经常会遇到一些重要的类型提示工具,例如 TypeVarGenericOptionalAnnotated。以下是它们的简要说明及使用场景:

  • Optional: 表示某变量可以是指定类型或 None。例如:

    from typing import Optional
    def greet(name: Optional[str] = None):
        if name is None:
            return "Hello, Guest!"
        return f"Hello, {name}!"
  • Annotated: 用于为类型提示添加元数据,提供更多上下文信息。例如:

    from typing import Annotated
    def process(value: Annotated[int, "positive"]):
        if value <= 0:
            raise ValueError("Value must be positive")
  • TypeVar: 用于定义可变的类型变量,通常与泛型一起使用。例如:

    from typing import TypeVar
    T = TypeVar('T')
  • Generic: 用于创建泛型类,支持类型参数化。例如:

    from typing import Generic, TypeVar
    T = TypeVar('T')
    class Box(Generic[T]):
        def __init__(self, content: T):
            self.content = content

这些工具能够帮助开发者更清晰地定义类型,减少运行时错误,同时提升代码的可读性和可维护性。在实际项目中,合理运用这些类型提示工具将极大改善代码质量。

具体场景分析

Optional

你给出了from typing import Optional,这是 Python 中用来导入 Optional 类型注解的语句。Optional 用于表示一个参数或返回值可以是某种特定类型,也可以是 None。

from enum import Enum
from typing import Optional

class RoleEnum(str, Enum):
    super = 'super'
    admin = 'admin'
    user = 'user'

def greet(name: Optional[list[RoleEnum]] | str = None):
    if name is None:
        return "Hello, stranger!"
    elif isinstance(name, list):
        role_names = [role.value for role in name]
        return f"Hello, {', '.join(role_names)}!"
    elif isinstance(name, str):
        return f"Hello, {name}!"

print(greet())
print(greet([RoleEnum.admin]))
print(greet([RoleEnum.admin, RoleEnum.user]))
print(greet("Alice"))

在这里插入图片描述

  • RoleEnum(str, Enum):

RoleEnum 继承自 str 和 Enum,这意味着它是一个字符串枚举类。

  • name: Optional[list[RoleEnum]] | str = None

    1. None:Optional 是 Python typing 模块中的一个特殊类型, Optional[X] 等价于 X | None,意味着该参数可以是指定的类型 X,也可以是 None。在这里,X 是 list[RoleEnum]。
    2. list[RoleEnum]:这是一个包含 RoleEnum 类型元素的列表。RoleEnum 应该是一个自定义的枚举类型,在你之前的代码中定义了 RoleEnum 枚举类,包含 super、admin、user 等角色。
    3. str:即普通的字符串类型。
Enum

在 Python 里,Enum 是一个枚举类,它来自于 enum 模块。枚举是一种包含一组具名常量的类型,在程序里使用枚举能够让代码更具可读性和可维护性。

from enum import Enum

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

#---------------访问枚举成员---------------
print(Color.RED)  
print(Color.GREEN.name)  
print(Color.BLUE.value)  


#-----------------迭代枚举-----------------
for color in Color:
    print(color.name, color.value)
Annotated

用途是为类型注解添加额外的元数据

from typing import Annotated

Annotated[type, metadata]

其中,type 是原本的类型注解,metadata 是你想要附加的额外信息,可以是任意 Python 对象,如字符串、整数、自定义类的实例等。

from typing import Annotated

# 为 int 类型添加额外信息
UserId = Annotated[int, "用户的唯一标识符"]

def get_user_info(user_id: UserId):
    print(f"正在获取用户 ID 为 {user_id} 的信息")

# 调用函数
get_user_info(123)

在上述代码中,UserId 是一个使用 Annotated 定义的类型别名,它本质上还是 int 类型,但附带了额外的元数据 "用户的唯一标识符"。这个额外信息在类型检查时不会被使用,但可以在代码文档、调试或其他工具中提供更多的上下文信息。

在一些数据验证库中,可以利用 Annotated 提供的元数据来进行更复杂的验证。例如,使用 pydantic

from typing import Annotated
from pydantic import BaseModel, Field
# BaseModel 是 pydantic 中用于创建数据模型的基类,
# Field 用于为模型字段添加验证规则和其他元数据。

# 为 int 类型添加验证信息
PositiveInt = Annotated[int, Field(gt=0)] # gt=0 表示该字段的值必须大于 0

class Item(BaseModel):
    quantity: PositiveInt

# 验证数据
item = Item(quantity=5)
print(item)  # 输出: quantity=5

#  验证无效数据
try:
    item = Item(quantity=-1)
except ValueError as e:
    print(f"验证失败: {e}")

关于pydantic后期专门出一篇文章来介绍

  • Optional与Annotated
表格 Optional Annotated 定义 Optional[X] 等价于 Union[X, None] ,表示变量可以是类型 X 或者 None Annotated[SomeType, metadata],表示在基本类型 SomeType 上附加元数据 metadata,不影响基本类型。 用途 明确表示变量可以接受 None 作为值,用于类型检查工具识别变量可能为 的情况。 在类型注解中附加额外信息(如验证规则、说明、序列化信息等),不影响类型检查结果,主要用于运行时处理或框架扩展。 是否影响类型检查 是,会改变类型检查的行为,例如允许变量为 None 。 否,不会改变类型检查结果,类型检查仍然基于基本类型。 使用场景 适用于函数参数、返回值或变量的类型注解,当变量可以为 None 时使用。 适用于需要在类型注解中添加额外元数据的场景,如数据验证、序列化、框架扩展等。 语法示例 Optional[int] 表示变量可以是整数或 None Annotated[str, "附加信息"] 表示变量是字符串,同时附加了说明信息。 版本支持 从 Python 3.5 开始支持( 模块)。 从 Python 3.9 开始支持( 模块)。 运行时行为 在运行时, Optional 类型的变量可以被赋值为 None 或指定类型。 在运行时, Annotated 的元数据可以通过特定工具或框架访问,但基本类型行为不变。
TypeVar

TypeVar 是 Python typing 模块中的一个工具,用于定义泛型类型变量。它允许在类型提示中使用变量来表示类型,而不是具体的类型。

  1. 泛型函数:可以定义一个函数,使其能够接受多种类型的参数并返回相同类型的值,同时保持类型安全
from typing import TypeVar

T = TypeVar('T')

def get_first_element(lst: list[T]) -> T:
    return lst[0]

# 使用整数列表
numbers = [1, 2, 3]
first_number = get_first_element(numbers)
print(first_number)  

# 使用字符串列表
strings = ["apple""banana""cherry"]
first_string = get_first_element(strings)
print(first_string) 

在上述代码中,T 是一个类型变量,get_first_element 函数能够接受任意类型的列表,并且返回该列表的第一个元素,其类型和列表元素的类型相同。

  1. 泛型类:在定义类时,若类的属性或者方法不依赖于具体的类型,也可以使用 TypeVar 实现泛型类。
from typing import TypeVar

T = TypeVar('T')

class Container:
    def __init__(self, value: T):
        self.value = value

    def get_value(self) -> T:
        return self.value

# 创建一个整数容器
int_container = Container(10)
print(int_container.get_value())  

# 创建一个字符串容器
str_container = Container("hello")
print(str_container.get_value()) 

10

hello

Generic

Generic 是 Python typing 模块中的一个工具,用于定义泛型类或泛型函数。它的主要作用是允许类或函数在类型提示中支持泛型,从而提高代码的灵活性和类型安全性。

from typing import TypeVar, Generic

K = TypeVar('K')
V = TypeVar('V')

class Pair(Generic[K, V]):
    def __init__(self, key: K, value: V):
        self.key = key
        self.value = value

Pair 类接受两个类型参数 K 和 V,分别用于表示键和值的类型

  • Generic 的工作原理

    1. Generic 是一个基类,它允许类在定义时接受类型参数。
    2. 当一个类继承自 Generic[T] 时,它就变成了一个泛型类,可以接受类型参数 T。
    3. 类型检查工具会根据传入的类型参数来推断类中方法的类型。
  • 有无 Generic 的对比

如果没有使用 Generic,类型检查工具可能无法正确识别类的泛型特性,导致类型检查失败或不准确。

  • 什么是类型检查工具

类型检查工具(Type Checking Tools)是一类软件工具,用于在代码编写或运行前验证代码的类型正确性。这些工具通过分析代码中的类型注解(Type Hints),检查变量、函数参数、返回值等是否符合预期的类型,从而帮助开发者发现潜在的类型错误,提高代码的质量和可维护性。

from typing import Generic, TypeVar

T = TypeVar('T')

class Box(Generic[T]):
    def __init__(self, content: T):
        self.content = content

box1 = Box(10)  # 没有显式指定类型参数
box2 = Box[str]("hello")  # 显式指定类型参数
print(box1.content)
print(box2.content)

# 错误示例
box1.content = 13  # mypy 不会报错,因为类型未显式指定
print(box1.content)
box2.content = 10  # mypy 会报错,因为类型不匹配
print(box2.content)

直接在Pycharm中运行并不会报错 但是实际上团队合作中类型指定时,要对应

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胜天半月子

打不打商的无所谓,能帮到你就好

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值