Python & AI Tutorials Logo
Python 编程

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 在大规模应用中越来越流行,团队遇到了一些挑战:

python
# 在大型代码库中,这个函数期望什么并返回什么?
def calculate_discount(customer, items, code):
    # ... 50 lines of code ...
    return result

在不阅读整个函数体或其文档的情况下,你无法知道:

  • customer 是字典、自定义对象,还是别的什么?
  • items 是列表、元组,还是集合?
  • code 是什么类型——字符串、整数?
  • 函数返回什么——数字、字典,还是可能是 None

在小型程序中,这种模糊性是可以管理的。你可以很容易地查看函数在其他地方是如何被使用的。但在一个包含数千个函数、分布在数十个文件中的代码库里,这就变得困难了。

解决方案:可选的类型提示

Python 的创建者决定添加一个用于记录类型的 可选(optional) 系统。关键字是“可选”——类型提示完全是自愿的。它们有帮助时你就用,没帮助时就不用,而且你可以自由混合带注解和不带注解的代码。

下面是一个简单示例来展示基本语法:

python
# 不使用类型提示
def add(a, b):
    return a + b
 
# 使用类型提示
def add(a: int, b: int) -> int:
    return a + b

语法很直接:

  • 在参数后写 冒号(:) 表示它应该是什么类型:a: int
  • 在冒号前写 箭头(->) 表示函数返回什么类型:-> int

现在我们用之前的例子来看看:

python
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 的类型提示系统被设计为:

  1. 可选(Optional):你永远不必使用类型提示
  2. 渐进(Gradual):你可以只给代码的一部分加提示,而不是全部
  3. 非侵入(Non-intrusive):提示不会改变 Python 执行代码的方式
  4. 工具友好(Tool-friendly):外部工具可以检查提示,但 Python 自身会在运行时忽略它们

这种务实的方法让 Python 仍然保持灵活,同时也为希望使用这些好处的人提供了收益。

不用了

好的

Python 代码

添加类型提示?

✓ 完全正常运行
✓ 保持简单

添加类型提示

✓ 运行时行为不变

✓ 更好的 IDE 支持

✓ 更早发现错误

42.2) 黄金法则:运行时不强制

关于类型提示最重要的一点是:Python 不会在运行时强制执行类型提示。它们纯粹是信息性的。让我们看看这个令人震惊的现实在实践中意味着什么。

类型提示不会阻止错误类型

考虑这个带类型提示的函数:

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 的动态类型也意味着你仍然可能犯类型错误,并且这些错误只会在运行时出现:

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 本身提供信息:

  1. 文档(Documentation):它们告诉你函数期望什么类型、返回什么类型
  2. IDE 支持(IDE Support):编辑器可以使用提示提供自动补全并显示警告
  3. 静态分析(Static Analysis):外部工具(如 mypy)可以在你运行代码 之前 检查类型错误
  4. 代码理解(Code Understanding):它们让大型代码库更易读、更易维护

把类型提示想成 工具能理解的注释(comments that tools can understand)。它们不会改变 Python 如何运行,但会帮助你写出更好的代码。

但这到底如何帮助我们捕获刚才看到的运行时错误呢?

解决方案:类型提示 + IDE 支持

这正是类型提示真正发光的地方。尽管 Python 不会在运行时强制它们,你的 IDE 可以在你运行代码之前就捕捉错误

python
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 开发通常是这样工作的:

  1. 你用类型提示编写代码
  2. 你的 IDE 在类型不匹配时显示警告
  3. 你在运行代码之前修复问题
  4. 由类型不匹配导致的运行时错误会变得更少见

类型提示并不会在运行时阻止错误,但你的 IDE 会利用它来阻止你一开始就写出有 bug 的代码!

两全其美

类型提示让 Python 达到两全其美——在保持灵活性的同时,尽早捕捉大多数错误:

开发安全(Development Safety):你的 IDE 和类型检查器会在开发阶段捕捉大多数类型错误,所以你能更早发现 bug。

python
def process(data: list) -> list:
    return [x * 2 for x in data]
 
# 如果你不小心传入了字符串:
process("hello")  # IDE warns: expected list, got str
# 你会在运行代码前就修复它!

运行时灵活(Runtime Flexibility):Python 仍然可以运行类型不匹配的代码,这对快速原型或你有意想接收多种类型时很有用。

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) 为函数添加注解:参数与返回值

类型提示最常见的用法,是给函数参数和返回值加注解。它能告诉读者(以及工具)一个函数期望的类型和产生的类型。我们从最简单的情况开始,逐步构建。

基础:参数注解

要为参数添加类型提示,在参数名后加一个冒号,然后写类型:

python
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 应该是一个字符串”。你可以为多个参数添加类型提示:

python
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

这里,widthheight 都被注解为 float。函数仍然像以前一样工作——类型提示不会改变行为——但现在你的 IDE 知道该期望什么类型了。

添加返回类型注解

要指定函数返回什么类型,在参数列表后、冒号前添加 -> type

python
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 的意思是“这个函数返回一个字符串”。当返回类型从函数名并不明显时,返回类型注解尤其有帮助:

python
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

无需查看实现,你就能立刻知道这个函数返回一个布尔值。

组合起来:一个完整的函数

大多数函数会同时包含参数与返回类型注解。下面是一个完整注解的函数是什么样:

python
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 参数:pricediscount_percent
  • 它返回一个 float
  • 你不需要阅读实现就能理解如何使用这个函数

再来看一个不同类型的例子:

python
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! 

类型提示清楚地表明:你传入一个字符串和一个整数,并返回一个字符串。

处理默认值

当参数有默认值时,把类型提示写在参数名和默认值之间:

python
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”。

你可以有多个带默认值的参数,并且都带注解:

python
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

python
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) 简单的变量注解

虽然类型提示最常用于函数,但你也可以给变量添加注解。让我们看看它如何工作,以及它在什么情况下真的有用。

基本变量注解语法

要注解一个变量,使用与函数参数相同的冒号语法:

python
# 注解变量
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 可以从值推断类型,所以注解通常是冗余的:

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 代码中,你很少会看到变量注解。 这很正常也符合预期。函数注解要重要得多,也更常见。

一个真正有用的情况:在赋值前声明变量

有一种情况下,变量注解确实很有用:当你需要在为变量赋值之前先声明它

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

如果没有注解,你无法在不同时赋值的情况下声明一个变量。注解让你可以提前指定类型,从而让代码结构更清晰。

这就是变量注解的主要实际用例。

记住:变量仍然可以被重新赋值为不同类型

即使有类型注解,你也可以把变量重新赋值为其他类型:

python
# 从字符串开始
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 的函数

考虑这个按邮箱搜索用户的函数:

python
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 引入了用于类型提示的 | 运算符,表示“或”。你可以用它表示函数可能返回一种类型或另一种类型:

python
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。它们含义相同,但 | 是现代的首选语法。

将 | 用于多种类型

你可以用 | 表示超过两种可能类型:

python
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 当作期望类型去使用时,就有出错风险:

python
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

你也可以把 | 用在参数上,通常与默认值结合:

python
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](首字母大写)——它们作用相同。

基本集合类型提示

最简单的集合类型提示只指定集合类型:

python
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)

这些提示告诉你集合类型,但不说明里面是什么。

列表:指定元素类型

要指定列表中元素的类型,使用方括号:

python
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 更有信息量。

再看一个字符串的例子:

python
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] 的意思是“包含字符串的列表”。

字典:指定键和值的类型

对于字典,需要同时指定键类型和值类型:

python
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] 的意思是“键为字符串、值为整数的字典”。

下面是一个值可以是多种类型的例子:

python
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] 的意思是“键为字符串、值可能是字符串或整数的字典”。

元组:固定长度与可变长度

元组与列表不同,因为它们通常有固定结构。你可以为每个位置指定类型:

python
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] 的意思是“恰好包含三个元素的元组:按顺序分别是字符串、整数、布尔值”。

对于长度可变但元素类型相同的元组,使用省略号(...):

python
# 固定长度元组:恰好 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 的元组”。... 的意思是“任意数量的该类型”。

嵌套集合

你可以嵌套类型提示来表示复杂数据结构。我们从一个简单例子开始:

python
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]] 的意思是“键为字符串、值为整数列表的字典”。

再来看一个更复杂的例子:

python
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。它们含义相同。

© 2025. Primesoft Co., Ltd.
support@primesoft.ai