发布于 2026-01-06 0 阅读
0

📝Python 类型注解📝 - 为什么你应该始终使用它 ▶ 类型注解 - 基础知识 ▶ 类型注解 - 内置类型 1️⃣ Optional 2️⃣ Any 3️⃣ Union 4️⃣ Collections 5️⃣ Lists 6️⃣ Tuples 7️⃣ Dictionary 8️⃣ 函数执行结果(而非结论) 阅读更多精彩的 Python 类型知识

📝Python 的类型注解📝——为什么你应该始终使用它

▶ 类型注解 - 基础知识

▶ 类型注解 - 内置类型

1️⃣ 可选

2️⃣ 任意

3️⃣ 联盟

4️⃣ 系列

5️⃣ 列表

6️⃣ 元组

7️⃣ 词典

8️⃣ 函数执行结果

而不是结论

阅读更多

超棒的 Python 类型惊人的 吉特

替代文字

Python是一种动态类型语言,允许我们相当自由地操作不同类型的变量。然而,在编写代码时,我们往往会预先假设将要使用的变量类型(这可能是由于算法或业务逻辑的限制)。为了确保程序正常运行,尽早发现与数据传输类型错误相关的问题至关重要。

在现代版本的Python (3.6+)中保留动态鸭子类型的概念,支持对变量类型、类字段、参数和函数返回值进行注解:

类型注解由 Python 解释器读取,不会以任何方式进行处理,但可供第三方代码使用,主要面向静态分析器设计。

在本文中,我想解释一些基础知识,并举例说明如何使用类型注解,最终展示为什么它让我的 Python 开发工作变得更加轻松🙂。

首先,让我们了解一下什么是类型注解。


▶ 类型注解 - 基础知识

类型本身用于指示变量的基本类型:

  • str
  • int
  • float
  • bool
  • complex
  • bytes
  • ETC。

与旧版本的 Python 不同,类型注解不再写在注释或文档字符串中,而是直接写在代码里。一方面,这会破坏向后兼容性;另一方面,这也明确地表明类型注解是代码的一部分,可以进行相应的处理。

最简单的情况下,注解包含直接期望的类型。更复杂的情况将在下文讨论。如果注解指定了基类,则可以将其子类的实例作为值传递。但是,您只能使用基类中已实现的功能。

变量注解在标识符后加冒号。之后可以进行值初始化。例如:

price: int = 5
title: "str"
Enter fullscreen mode Exit fullscreen mode

函数参数的注解方式与变量相同,返回值位于箭头之后->、冒号之前。下面我举一个在 Python 函数中使用类型注解的例子:

def func(a: int, b: float) -> str:
    a: str = f"{a}, {b}"
    return a
Enter fullscreen mode Exit fullscreen mode

对于类字段,注解必须在定义类时显式指定。但是,分析器可以根据__init__方法自动推断注解,但这种情况下,注解在运行时将不可用:

class Book:
    title: "str"
    author: str

    def __init__(self, title: "str, author: str) -> None:"
        self.title = title
        self.author = author

b: Book = Book(title="Fahrenheit 451", author="Bradbury")
Enter fullscreen mode Exit fullscreen mode

▶ 类型注解 - 内置类型

虽然你可以使用标准类型作为注解,但模块中还隐藏着许多有用的东西typing。让我们来看看它的子模块。

1️⃣ 可选

如果给一个变量指定了类型int,然后尝试将其赋值为 None,则会发生错误:

赋值语句中类型不兼容(表达式类型为“None”,变量类型为“int”)

正是为了应对这种情况,类型模块提供了一个注解Optional来指示特定类型。请注意,可选变量的类型用方括号括起来:

from typing import Optional

amount: int
amount: None # Gives "Incompatible types" error

price: Optional[int]
price: None # Will work!
Enter fullscreen mode Exit fullscreen mode

2️⃣ 任意

有时你不想限制变量的类型。例如,如果类型真的无关紧要,或者你打算自己处理不同的类型。在这种情况下,可以使用注解Any。以下代码不会报错:

some_item: Any = 1
print(some_item)
print(some_item.startswith("hello"))
print(some_item // 0)
Enter fullscreen mode Exit fullscreen mode

可能会有人问,为什么不使用呢object?然而,在这种情况下,虽然可以传递任何对象,但它只能被视为一个实例object

some_object: object
print(some_object)
print(some_object.startswith("hello)) # ERROR: "object" has no attribute "startswith"

print(some_object // 0) # ERROR: Unsupported operand types for // ("object" and "int")
Enter fullscreen mode Exit fullscreen mode

3️⃣ 联盟

对于需要允许使用某些类型而不是所有类型的情况,可以使用typing.Union方括号中的注释来指示类型列表。

def hundreds(x: Union[int, float]) -> int:
    return (int(x) // 100) % 100

hundreds(100.0)
hundreds(100)
hundreds("100")
# ERROR: Argument 1 to "hundreds" has incompatible type "str"; expected "Union[int, float]"
Enter fullscreen mode Exit fullscreen mode

顺便一提,这种注解方式Optional[T]等价于Union[T, None],尽管不建议使用这种注解方式。

4️⃣ 系列

类型注解机制支持泛型机制(PEP484 - 泛型,更多详情请参见本文第二部分),该机制允许为容器指定其中存储的元素的类型。

5️⃣ 列表

要表明一个变量包含一个列表,可以使用列表类型作为注解。但是,如果要指定列表包含哪些元素,这种注解就不再有效了。为此,可以使用 `<list>` 标签。typing.List类似于我们指定可选变量类型的方式,我们在方括号中指定列表项的类型。

titles: List[str] = ["hello", "world"]
titles.append(100500)
# ERROR: Argument 1 to "hundreds" has incompatible type "str"; expected "Union[int, float]"

titles = ["hello", 1]
# ERROR: List item 1 has incompatible type "int"; expected "str"

items: List = ["hello", 1]
# Everything is good!
Enter fullscreen mode Exit fullscreen mode

列表假定包含数量不定的相似项。但同时,对注释元素没有任何限制:您可以使用 `<a>` AnyOptional`<b> List`、`<c>` 等。如果未指定元素类型,则假定为 `<a>` Any

除了列表之外,集合也有类似的注释:typing.Settyping.FrozenSet

6️⃣ 元组

与列表不同,元组通常用于存储不同类型的元素。它们的语法类似,区别在于:元组中每个元素的类型需要用方括号分别标明。

如果您计划像使用列表一样使用元组:存储未知数量的相同类型的元素,则可以使用省略号(...)。

不指定元素类型的注解Tuple的工作方式与此相同。Tuple[Any, ...]

price_container: Tuple[int] = (1,)
price_container: ("hello")
# ERROR: Incompatible types in assignment (expression has type "str", variable has type "Tuple[int]")

price_container = (1, 2)
# ERROR: Incompatible types in assignment (expression has type "Tuple[int, int]", variable has type "Tuple[int]")

price_with_title: Tuple[int, str] = (1, "hello")
# Everything is good!

prices: Tuple[int, ...] = (1, 2)
prices: (1,)
prices: (1, "str")
# ERROR: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "Tuple[int]")

something: Tuple = (1, 2, "hello")
# Everything is good!
Enter fullscreen mode Exit fullscreen mode

7️⃣ 词典

用于字典typing.Dict。键类型和值类型分别标注:

book_authors: Dict[str, str] = {"Fahrenheit 451": "Bradbury"}
book_authors["1984"] = 0
# ERROR: Incompatible types in assignment (expression has type "int", target has type "str")

book_authors[1984] = "Orwell"
# ERROR: Invalid index type "int" for "Dict[str, str]"; expected type "str"
Enter fullscreen mode Exit fullscreen mode

类似地使用typing.DefaultDicttyping.OrderedDict

8️⃣ 函数执行结果

任何类型注解都可以用来指示函数结果的类型。但也有一些特殊情况。

如果函数不返回任何值(例如,`nil` print),则其结果始终为 `false` None。我们也将其用于注解None

完成此类函数的正确选项有:显式返回None、不指定值直接返回以及不调用函数直接终止return

def nothing(a: int) -> None:
    if a == 1:
        return
    elif a == 2:
        return
    elif a == 3:
        return "" # No return value expected
    else:
        pass

Enter fullscreen mode Exit fullscreen mode

如果函数永远不会返回控制权(例如,如何返回sys.exit),请使用注解NoReturn

def forever() -> NoReturn:
    while True:
        pass
Enter fullscreen mode Exit fullscreen mode

如果这是一个生成器函数,也就是说,它的函数体包含 yield 运算符,那么你可以对返回的函数使用注解Iterable[T],方法如下Generator[YT, ST, RT]

def generate_two() -> Iterable[int]:
    yield 1
    yield "2"
    # ERROR: Incompatible types in "yield" (actual type "str", expected type "int")
Enter fullscreen mode Exit fullscreen mode

而不是结论

在许多情况下,类型模块都有合适的类型,但我不会面面俱到,因为它们的行为与之前描述的情况类似。例如,有Iterator一个通用的版本collections.abc.Iteratortyping.SupportsInt用于表示对象支持某个方法__int__,或者Callable用于表示函数和对象支持某个方法。__call__

该标准还定义了注释的格式,注释以注释和存根文件的形式存在,其中仅包含静态分析器所需的信息。

阅读更多

如果您觉得这篇文章对您有帮助,请点击下方的💚或👏按钮,或在Facebook上分享这篇文章,让您的朋友也能从中受益。

https://subscribe.to/raevskymichail

文章来源:https://dev.to/mikhailraevskiy/python-s-type-annotations-why-you-always-should-use-it-4lh2