内容来源公众号:胜天半月子
学习typing模块
在学习 typing
模块及其扩展时,我们经常会遇到一些重要的类型提示工具,例如 TypeVar
、Generic
、Optional
和 Annotated
。以下是它们的简要说明及使用场景:
-
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
-
None:Optional 是 Python typing 模块中的一个特殊类型, Optional[X] 等价于 X | None
,意味着该参数可以是指定的类型 X,也可以是 None。在这里,X 是 list[RoleEnum]。 -
list[RoleEnum]:这是一个包含 RoleEnum 类型元素的列表。RoleEnum 应该是一个自定义的枚举类型,在你之前的代码中定义了 RoleEnum 枚举类,包含 super、admin、user 等角色。 -
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[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 模块中的一个工具,用于定义泛型类型变量。它允许在类型提示中使用变量来表示类型,而不是具体的类型。
-
泛型函数:可以定义一个函数,使其能够接受多种类型的参数并返回相同类型的值,同时保持类型安全
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 函数能够接受任意类型的列表,并且返回该列表的第一个元素,其类型和列表元素的类型相同。
-
泛型类:在定义类时,若类的属性或者方法不依赖于具体的类型,也可以使用 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 的工作原理
-
Generic 是一个基类,它允许类在定义时接受类型参数。 -
当一个类继承自 Generic[T]
时,它就变成了一个泛型类,可以接受类型参数 T。 -
类型检查工具会根据传入的类型参数来推断类中方法的类型。
-
-
有无
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中运行并不会报错 但是实际上团队合作中类型指定时,要对应
