42. 类型提示入门(可选)
在本书中,你一直在编写 Python 代码,而没有指定变量保存的数据类型,或函数接受与返回的类型。Python 以这种方式也能完美运行——它是一门 动态类型(dynamically typed) 语言,这意味着类型是在程序执行的运行时(runtime)确定的。这种灵活性是 Python 最大的优势之一,让你能够快速而富有表现力地编写代码。
然而,随着程序变得更大、更复杂,这种灵活性有时会让代码更难理解和维护。当你看到一个函数,比如 def process_data(items):,你可能会想:items 包含的是什么类型的数据?字符串列表?字典?还是完全不同的东西?
类型提示(type hints)(也叫 类型注解(type annotations))提供了一种方式,用来记录代码中预期的类型。它们是对 Python 的可选补充,能让你的代码更清晰、更早发现错误,并启用强大的 IDE 功能——同时完全不改变 Python 实际运行你的代码的方式。
本章会温和地介绍类型提示:它们是什么、为何存在、以及如何有效使用它们。因为类型提示是可选的,并且不会影响 Python 如何执行你的代码,所以整章都被标记为可选。即使完全不使用类型提示,你也能写出优秀的 Python 程序。但理解它们将帮助你阅读现代 Python 代码,并决定何时它们可能对你自己的项目有帮助。
42.1) 为什么 Python 要加入类型提示
从一开始,Python 就被设计为动态类型语言。几十年来,Python 程序员在没有任何类型信息的情况下编写代码,并且这在无数项目中都运行得非常好。那么,为什么 Python 会在 2015 年(随 Python 3.5)加入类型提示呢?
大型代码库的挑战
随着 Python 在大规模应用中越来越流行,团队遇到了一些挑战:
# 在大型代码库中,这个函数期望什么并返回什么?
def calculate_discount(customer, items, code):
# ... 50 lines of code ...
return result在不阅读整个函数体或其文档的情况下,你无法知道:
customer是字典、自定义对象,还是别的什么?items是列表、元组,还是集合?code是什么类型——字符串、整数?- 函数返回什么——数字、字典,还是可能是
None?
在小型程序中,这种模糊性是可以管理的。你可以很容易地查看函数在其他地方是如何被使用的。但在一个包含数千个函数、分布在数十个文件中的代码库里,这就变得困难了。
解决方案:可选的类型提示
Python 的创建者决定添加一个用于记录类型的 可选(optional) 系统。关键字是“可选”——类型提示完全是自愿的。它们有帮助时你就用,没帮助时就不用,而且你可以自由混合带注解和不带注解的代码。
下面是一个简单示例来展示基本语法:
# 不使用类型提示
def add(a, b):
return a + b
# 使用类型提示
def add(a: int, b: int) -> int:
return a + b语法很直接:
- 在参数后写 冒号(
:) 表示它应该是什么类型:a: int - 在冒号前写 箭头(
->) 表示函数返回什么类型:-> int
现在我们用之前的例子来看看:
def calculate_discount(customer: dict, items: list, code: str) -> float:
# ... 50 lines of code ...
return result现在一眼就能看清:customer 是字典,items 是列表,code 是字符串,并且函数返回一个 float。
如果这套语法看起来不熟悉也不用担心——我们会在 42.3-42.6 节中详细探讨。现在,你只需要注意:你可以一眼看出函数期望什么,以及它返回什么。
有或没有类型提示,函数都以完全相同的方式工作——Python 不会在运行时检查这些类型。(我们会在 42.2 节详细探讨这一重要点)
渐进且务实的方法
Python 的类型提示系统被设计为:
- 可选(Optional):你永远不必使用类型提示
- 渐进(Gradual):你可以只给代码的一部分加提示,而不是全部
- 非侵入(Non-intrusive):提示不会改变 Python 执行代码的方式
- 工具友好(Tool-friendly):外部工具可以检查提示,但 Python 自身会在运行时忽略它们
这种务实的方法让 Python 仍然保持灵活,同时也为希望使用这些好处的人提供了收益。
42.2) 黄金法则:运行时不强制
关于类型提示最重要的一点是:Python 不会在运行时强制执行类型提示。它们纯粹是信息性的。让我们看看这个令人震惊的现实在实践中意味着什么。
类型提示不会阻止错误类型
考虑这个带类型提示的函数:
def greet(name: str) -> str:
return f"Hello, {name}!"
# 即使 42 不是字符串,这也能正常工作
result = greet(42)
print(result) # Output: Hello, 42!类型提示清楚地写着 name 应该是字符串,但 Python 仍然愉快地接受整数 42 并运行函数。Python 不会检查类型提示——它只会使用你提供的值。
这与 Java 或 C++ 之类的语言有根本差异:那些语言的编译器会在运行代码之前检查类型,如果类型不匹配就拒绝运行。Python 的方式更宽松:它相信你会提供正确的类型,但不会强制你这样做。
问题:动态类型的风险依然存在
真正的挑战在于:即使有类型提示,Python 的动态类型也意味着你仍然可能犯类型错误,并且这些错误只会在运行时出现:
def calculate_total(prices: list) -> float:
"""Calculate the sum of prices."""
return sum(prices)
# 这没问题
print(calculate_total([10.99, 5.50, 3.25])) # Output: 19.74
# 但这会在运行时失败!
print(calculate_total("not a list")) # TypeError: 'str' object is not iterable类型提示清楚地说 prices 应该是列表,但 Python 不会阻止你传入一个字符串。错误只会在代码真正运行、并尝试对字符串使用 sum() 时才出现。
这很令人沮丧! 我们添加类型提示是为了捕捉这些问题,但动态类型的风险仍然存在。类型错误可能潜伏在你的代码里直到运行时才暴露,甚至可能在生产环境中、当用户做了某些意外操作时才出现。
所以如果类型提示不能阻止运行时错误,那使用它们的意义是什么?
那类型提示到底是做什么用的?
类型提示可能不会改变 Python 的运行时行为,但它们有一个关键用途——它们为 人和工具 提供信息,而不是为 Python 本身提供信息:
- 文档(Documentation):它们告诉你函数期望什么类型、返回什么类型
- IDE 支持(IDE Support):编辑器可以使用提示提供自动补全并显示警告
- 静态分析(Static Analysis):外部工具(如 mypy)可以在你运行代码 之前 检查类型错误
- 代码理解(Code Understanding):它们让大型代码库更易读、更易维护
把类型提示想成 工具能理解的注释(comments that tools can understand)。它们不会改变 Python 如何运行,但会帮助你写出更好的代码。
但这到底如何帮助我们捕获刚才看到的运行时错误呢?
解决方案:类型提示 + IDE 支持
这正是类型提示真正发光的地方。尽管 Python 不会在运行时强制它们,你的 IDE 可以在你运行代码之前就捕捉错误:
def add_numbers(a: int, b: int) -> int:
"""Add two numbers."""
return a + b
# 你的 IDE 会在这里显示警告(在你运行代码之前)
result = add_numbers("Hello", "World") # IDE: Warning - expected int, got str你的代码编辑器看到了类型提示,就可以在你输入时警告类型不匹配的问题,远在你运行代码之前。这能在开发阶段捕捉很多 bug,而不是等到生产环境才发现。
现代 Python 开发通常是这样工作的:
- 你用类型提示编写代码
- 你的 IDE 在类型不匹配时显示警告
- 你在运行代码之前修复问题
- 由类型不匹配导致的运行时错误会变得更少见
类型提示并不会在运行时阻止错误,但你的 IDE 会利用它来阻止你一开始就写出有 bug 的代码!
两全其美
类型提示让 Python 达到两全其美——在保持灵活性的同时,尽早捕捉大多数错误:
开发安全(Development Safety):你的 IDE 和类型检查器会在开发阶段捕捉大多数类型错误,所以你能更早发现 bug。
def process(data: list) -> list:
return [x * 2 for x in data]
# 如果你不小心传入了字符串:
process("hello") # IDE warns: expected list, got str
# 你会在运行代码前就修复它!运行时灵活(Runtime Flexibility):Python 仍然可以运行类型不匹配的代码,这对快速原型或你有意想接收多种类型时很有用。
def add_numbers(a: int, b: int) -> int:
return a + b
# 即使类型不匹配,Python 也会运行
print(add_numbers(5.5, 3.2)) # Output: 8.7 (works!)
print(add_numbers("Hi", " there")) # Output: Hi there (also works!)这种灵活性意味着你不会被锁定在一个僵硬的类型系统里。当你需要打破规则时(用于测试、原型设计或合理的使用场景),Python 会允许你。但当你编写生产代码时,你的 IDE 会保证你的安全。
记住黄金法则:类型提示不会改变 Python 的运行时行为——它们只是为你和你的工具提供了及早发现问题所需的信息。你仍然需要小心,但现在你有强大的盟友在背后盯着。
42.3) 为函数添加注解:参数与返回值
类型提示最常见的用法,是给函数参数和返回值加注解。它能告诉读者(以及工具)一个函数期望的类型和产生的类型。我们从最简单的情况开始,逐步构建。
基础:参数注解
要为参数添加类型提示,在参数名后加一个冒号,然后写类型:
def greet(name: str):
"""Greet a person by name."""
return f"Hello, {name}!"
# Usage
message = greet("Alice")
print(message) # Output: Hello, Alice!语法 name: str 的意思是“参数 name 应该是一个字符串”。你可以为多个参数添加类型提示:
def calculate_area(width: float, height: float):
"""Calculate the area of a rectangle."""
return width * height
# Usage
area = calculate_area(5.0, 3.0)
print(area) # Output: 15.0这里,width 和 height 都被注解为 float。函数仍然像以前一样工作——类型提示不会改变行为——但现在你的 IDE 知道该期望什么类型了。
添加返回类型注解
要指定函数返回什么类型,在参数列表后、冒号前添加 -> type:
def get_full_name(first: str, last: str) -> str:
"""Combine first and last names."""
return f"{first} {last}"
# Usage
name = get_full_name("John", "Doe")
print(name) # Output: John Doe-> str 的意思是“这个函数返回一个字符串”。当返回类型从函数名并不明显时,返回类型注解尤其有帮助:
def is_adult(age: int) -> bool:
"""Check if someone is an adult (18 or older)."""
return age >= 18
# Usage
adult = is_adult(25)
print(adult) # Output: True无需查看实现,你就能立刻知道这个函数返回一个布尔值。
组合起来:一个完整的函数
大多数函数会同时包含参数与返回类型注解。下面是一个完整注解的函数是什么样:
def calculate_discount(price: float, discount_percent: float) -> float:
"""Calculate the discounted price."""
discount_amount = price * (discount_percent / 100)
return price - discount_amount
# Usage
original_price = 100.0
discount = 20.0
final_price = calculate_discount(original_price, discount)
print(f"Final price: ${final_price:.2f}") # Output: Final price: $80.00这个函数签名告诉你需要知道的一切:
- 它接收两个
float参数:price和discount_percent - 它返回一个
float值 - 你不需要阅读实现就能理解如何使用这个函数
再来看一个不同类型的例子:
def repeat_message(message: str, times: int) -> str:
"""Repeat a message a specified number of times."""
return message * times
# Usage
repeated = repeat_message("Hello! ", 3)
print(repeated) # Output: Hello! Hello! Hello! 类型提示清楚地表明:你传入一个字符串和一个整数,并返回一个字符串。
处理默认值
当参数有默认值时,把类型提示写在参数名和默认值之间:
def create_greeting(name: str, formal: bool = False) -> str:
"""Create a greeting message."""
if formal:
return f"Good day, {name}."
return f"Hi, {name}!"
# Usage
print(create_greeting("Alice")) # Output: Hi, Alice!
print(create_greeting("Bob", formal=True)) # Output: Good day, Bob.语法 formal: bool = False 的意思是“formal 是一个布尔值,默认值为 False”。
你可以有多个带默认值的参数,并且都带注解:
def format_price(amount: float, currency: str = "USD", decimals: int = 2) -> str:
"""Format a price with currency symbol."""
if currency == "USD":
symbol = "$"
elif currency == "EUR":
symbol = "€"
else:
symbol = currency
return f"{symbol}{amount:.{decimals}f}"
# Usage
print(format_price(99.99)) # Output: $99.99
print(format_price(99.99, "EUR")) # Output: €99.99
print(format_price(99.995, "USD", 3)) # Output: $99.995每个参数都清楚地展示了它的类型与默认值,使得函数易于理解和使用。
特殊情况:不返回值的函数
有些函数只执行动作(如打印或写入文件),而不返回值。为了明确这些函数不返回任何内容,使用 -> None:
def print_report(title: str, data: list) -> None:
"""Print a formatted report."""
print(f"=== {title} ===")
for item in data:
print(f" - {item}")
# 没有 return 语句,因此会隐式返回 None
# Usage
print_report("Sales Data", [100, 150, 200])Output:
=== Sales Data ===
- 100
- 150
- 200-> None 注解明确表示这个函数不会返回有意义的值。
为什么要用 -> None?
- 清晰(Clarity):它让你的意图更明确——这个函数是用于动作,而不是结果
- IDE 支持(IDE Support):如果你不小心尝试使用返回值,你的 IDE 可以警告你
42.4) 简单的变量注解
虽然类型提示最常用于函数,但你也可以给变量添加注解。让我们看看它如何工作,以及它在什么情况下真的有用。
基本变量注解语法
要注解一个变量,使用与函数参数相同的冒号语法:
# 注解变量
name: str = "Alice"
age: int = 30
height: float = 5.7
is_student: bool = True
print(f"{name} is {age} years old") # Output: Alice is 30 years old语法 name: str = "Alice" 的意思是“变量 name 是一个字符串,并且值是 'Alice'”。注解不会改变变量的行为——它纯粹是信息性的。
变量注解经常会被跳过
在实践中,变量注解很少使用。原因很简单:Python 可以从值推断类型,所以注解通常是冗余的:
# 这些注解是不必要的
name: str = "Alice" # 显然是字符串
count: int = 0 # 显然是 int
prices: list = [10.99, 5.50] # 显然是 list
settings: dict = {} # 显然是 dict
# 直接写这个就行
name = "Alice"
count = 0
prices = [10.99, 5.50]
settings = {}当你写 name = "Alice" 时,你和你的 IDE 立刻就知道它是字符串。注解并没有提供有用的信息。
在真实的 Python 代码中,你很少会看到变量注解。 这很正常也符合预期。函数注解要重要得多,也更常见。
一个真正有用的情况:在赋值前声明变量
有一种情况下,变量注解确实很有用:当你需要在为变量赋值之前先声明它。
def calculate_statistics(numbers: list) -> dict:
"""Calculate basic statistics from a list of numbers."""
# 在使用之前声明变量
total: float
count: int
average: float
# 现在再赋值
total = sum(numbers)
count = len(numbers)
average = total / count if count > 0 else 0.0
return {
"total": total,
"count": count,
"average": average
}
# Usage
result = calculate_statistics([10, 20, 30, 40])
print(f"Average: {result['average']}") # Output: Average: 25.0如果没有注解,你无法在不同时赋值的情况下声明一个变量。注解让你可以提前指定类型,从而让代码结构更清晰。
这就是变量注解的主要实际用例。
记住:变量仍然可以被重新赋值为不同类型
即使有类型注解,你也可以把变量重新赋值为其他类型:
# 从字符串开始
value: str = "hello"
print(value) # Output: hello
# 重新赋值为不同类型 - Python 允许这样做
value = 42
print(value) # Output: 42
# 再换一种类型 - 仍然允许
value = [1, 2, 3]
print(value) # Output: [1, 2, 3]你的 IDE 或静态类型检查器会对这些类型变化发出警告,但 Python 本身不会阻止。类型提示会引导你保持一致,但不会在运行时强制执行。
42.5) 处理 “None”:Optional 类型与 | 运算符
Python 中最常见的模式之一是:函数可能返回一个值,也可能返回 None。例如,搜索某个条目可能成功(返回该条目),也可能失败(返回 None)。类型提示提供了清晰的方式来表达这种模式。
问题:可能返回 None 的函数
考虑这个按邮箱搜索用户的函数:
def find_user_by_email(email: str) -> dict:
"""Find a user by email address."""
users = [
{"name": "Alice", "email": "alice@example.com"},
{"name": "Bob", "email": "bob@example.com"}
]
for user in users:
if user["email"] == email:
return user
return None # 类型不匹配!这与 -> dict 提示相矛盾
# Usage
user = find_user_by_email("alice@example.com")
if user:
print(f"Found: {user['name']}") # Output: Found: Alice
else:
print("User not found")类型提示 -> dict 具有误导性,因为函数可能返回 None。静态类型检查器会警告你:返回 None 与声明的返回类型 dict 不匹配。
解决方案:用 | 运算符表示 Optional 类型
Python 3.10 引入了用于类型提示的 | 运算符,表示“或”。你可以用它表示函数可能返回一种类型或另一种类型:
def find_user_by_email(email: str) -> dict | None:
"""Find a user by email address. Returns None if not found."""
users = [
{"name": "Alice", "email": "alice@example.com"},
{"name": "Bob", "email": "bob@example.com"}
]
for user in users:
if user["email"] == email:
return user
return None
# Usage
user = find_user_by_email("alice@example.com")
if user:
print(f"Found: {user['name']}") # Output: Found: Alice
missing = find_user_by_email("charlie@example.com")
if missing is None:
print("User not found") # Output: User not found类型提示 -> dict | None 的意思是“这个函数返回一个字典或 None”。这准确描述了函数行为。
注意: 在较旧的 Python 代码(3.10 之前)中,你可能会看到 typing 模块里的 Optional[dict],而不是 dict | None。它们含义相同,但 | 是现代的首选语法。
将 | 用于多种类型
你可以用 | 表示超过两种可能类型:
def parse_value(text: str) -> int | float | None:
"""Parse a string into a number. Returns None if parsing fails."""
try:
# 先尝试解析为整数
if '.' not in text:
return int(text)
# 否则解析为 float
return float(text)
except ValueError:
return None
# Usage
print(parse_value("42")) # Output: 42 (int)
print(parse_value("3.14")) # Output: 3.14 (float)
print(parse_value("invalid")) # Output: None类型提示 -> int | float | None 表示函数可以返回整数、浮点数或 None。
检查 None:最佳实践
当函数可能返回 None 时,在使用结果之前始终检查是否为 None。否则,当你把 None 当作期望类型去使用时,就有出错风险:
def get_user_age(user_id: int) -> int | None:
"""Get user's age. Returns None if user not found."""
users = {1: 25, 2: 30, 3: 35}
return users.get(user_id)
# 在使用该值之前始终检查 None
age = get_user_age(1)
if age is not None:
print(f"User is {age} years old") # Output: User is 25 years old
if age >= 18:
print("User is an adult") # Output: User is an adult
else:
print("User not found")
# 对于不存在的用户
age = get_user_age(999)
if age is None:
print("User not found") # Output: User not found关键在于使用 if age is not None: 或 if age is None: 来在使用之前明确检查。
可选参数:| None
你也可以把 | 用在参数上,通常与默认值结合:
def format_name(first: str, middle: str | None = None, last: str = "") -> str:
"""Format a full name. Middle name is optional."""
if middle and last:
return f"{first} {middle} {last}"
elif last:
return f"{first} {last}"
return first
# Usage
print(format_name("John", "Q", "Doe")) # Output: John Q Doe
print(format_name("Jane", None, "Smith")) # Output: Jane Smith
print(format_name("Prince")) # Output: Prince类型提示 middle: str | None = None 表示 middle 可以是字符串或 None,并且默认值为 None。这是可选参数的常见模式。
42.6) 读懂常见类型提示:list、dict、tuple
当你阅读其他人写的 Python 代码时,你会遇到列表、字典、元组等集合的类型提示。现代 Python 提供了清晰的方式,不仅可以指定它是列表,还可以指定列表里包含的元素类型。
注意: 这里展示的语法(list[int]、dict[str, int] 等)适用于 Python 3.9+。在更旧的代码中,你可能会看到 typing 模块中的 List[int] 和 Dict[str, int](首字母大写)——它们作用相同。
基本集合类型提示
最简单的集合类型提示只指定集合类型:
def print_items(items: list) -> None:
"""Print all items in a list."""
for item in items:
print(item)
def get_user_settings() -> dict:
"""Get user settings as a dictionary."""
return {"theme": "dark", "notifications": True}
def get_position() -> tuple:
"""Get x, y position."""
return (10, 20)这些提示告诉你集合类型,但不说明里面是什么。
列表:指定元素类型
要指定列表中元素的类型,使用方括号:
def calculate_total(prices: list[float]) -> float:
"""Calculate the total of all prices."""
return sum(prices)
# Usage
total = calculate_total([10.99, 5.50, 3.25])
print(f"Total: ${total:.2f}") # Output: Total: $19.74类型提示 list[float] 的意思是“包含 float 的列表”。这比只写 list 更有信息量。
再看一个字符串的例子:
def format_names(names: list[str]) -> str:
"""Format a list of names as a comma-separated string."""
return ", ".join(names)
# Usage
students = ["Alice", "Bob", "Charlie"]
print(format_names(students)) # Output: Alice, Bob, Charlie类型提示 list[str] 的意思是“包含字符串的列表”。
字典:指定键和值的类型
对于字典,需要同时指定键类型和值类型:
def get_student_grades() -> dict[str, int]:
"""Get student names mapped to their grades."""
return {
"Alice": 95,
"Bob": 87,
"Charlie": 92
}
# Usage
grades = get_student_grades()
for name, grade in grades.items():
print(f"{name}: {grade}")Output:
Alice: 95
Bob: 87
Charlie: 92类型提示 dict[str, int] 的意思是“键为字符串、值为整数的字典”。
下面是一个值可以是多种类型的例子:
def get_user_data(user_id: int) -> dict[str, str | int]:
"""Get user data. Values can be strings or integers."""
return {
"name": "Alice",
"email": "alice@example.com",
"age": 30,
"id": 12345
}
# Usage
user = get_user_data(1)
print(f"{user['name']} is {user['age']} years old") # Output: Alice is 30 years old类型提示 dict[str, str | int] 的意思是“键为字符串、值可能是字符串或整数的字典”。
元组:固定长度与可变长度
元组与列表不同,因为它们通常有固定结构。你可以为每个位置指定类型:
def get_user_info(user_id: int) -> tuple[str, int, bool]:
"""
Get user information as a tuple.
Returns: (name, age, is_active)
"""
return ("Alice", 30, True)
# Usage
name, age, active = get_user_info(1)
print(f"{name}, {age}, active: {active}") # Output: Alice, 30, active: True类型提示 tuple[str, int, bool] 的意思是“恰好包含三个元素的元组:按顺序分别是字符串、整数、布尔值”。
对于长度可变但元素类型相同的元组,使用省略号(...):
# 固定长度元组:恰好 2 个 float
def get_2d_point() -> tuple[float, float]:
"""Get 2D coordinates (x, y)."""
return (10.5, 20.3)
# 可变长度元组:任意数量的 float
def get_coordinates() -> tuple[float, ...]:
"""Get coordinates. Can be 2D, 3D, or any dimension."""
return (10.5, 20.3, 15.7) # 这里是 3D 的情况
# Usage
point = get_2d_point()
coords = get_coordinates()
print(f"2D point: {point}") # Output: 2D point: (10.5, 20.3)
print(f"Coordinates: {coords}") # Output: Coordinates: (10.5, 20.3, 15.7)类型提示 tuple[float, ...] 的意思是“包含任意数量 float 的元组”。... 的意思是“任意数量的该类型”。
嵌套集合
你可以嵌套类型提示来表示复杂数据结构。我们从一个简单例子开始:
def get_scores_by_student() -> dict[str, list[int]]:
"""Get test scores for each student."""
return {
"Alice": [95, 87, 92],
"Bob": [88, 91, 85],
"Charlie": [90, 88, 94]
}
# Usage
scores = get_scores_by_student()
for name, tests in scores.items():
average = sum(tests) / len(tests)
print(f"{name}: {average:.1f}")Output:
Alice: 91.3
Bob: 88.0
Charlie: 90.7类型提示 dict[str, list[int]] 的意思是“键为字符串、值为整数列表的字典”。
再来看一个更复杂的例子:
def get_student_records() -> list[dict[str, str | int]]:
"""Get a list of student records."""
return [
{"name": "Alice", "age": 20, "major": "CS"},
{"name": "Bob", "age": 21, "major": "Math"},
{"name": "Charlie", "age": 19, "major": "Physics"}
]
# Usage
students = get_student_records()
for student in students:
print(f"{student['name']}, {student['age']}, {student['major']}")Output:
Alice, 20, CS
Bob, 21, Math
Charlie, 19, Physics类型提示 list[dict[str, str | int]] 的意思是“由字典组成的列表,其中每个字典的键是字符串,而值要么是字符串要么是整数”。
读取类型提示:快速参考
当你在代码中遇到类型提示时,可以这样理解它们:
集合(Collections):
list[int]- “整数列表”dict[str, float]- “键为字符串、值为浮点数的字典”tuple[str, int]- “恰好两个元素的元组:先是字符串,再是整数”tuple[float, ...]- “包含任意数量浮点数的元组”
可选与多种类型(Optional and Multiple Types):
int | None- “整数或 None”str | int | float- “字符串、整数或浮点数”
嵌套(Nested):
list[dict[str, int]]- “字典列表(每个 dict 的键为字符串,值为整数)”dict[str, list[float]]- “键为字符串、值为浮点数列表的字典”
注意: 在旧代码(Python < 3.10)中,你可能会看到 Union[int, str] 而不是 int | str,或 Optional[int] 而不是 int | None。它们含义相同。