python - Python 3.5 中的类型提示是什么?

标签 python python-3.x python-3.5 type-hinting

Python 3.5 中最受关注的特性之一是 类型提示 .
的例子类型提示 this article 中提到和 this one同时还提到负责任地使用类型提示。有人可以解释更多关于它们的信息,何时应该使用,何时不使用?

最佳答案

我建议阅读 PEP 483PEP 484并观看 this presentation来自 Guido关于类型提示。
简而言之 :类型提示就是字面上的意思。您暗示了您正在使用的对象的类型。
由于 Python 的动态特性,推断或检查正在使用的对象的类型特别困难。这一事实使开发人员难以理解他们尚未编写的代码究竟发生了什么,最重要的是,对于许多 IDE(PyCharmPyDev)中发现的类型检查工具来说,这些工具是有限的事实上,他们没有任何关于对象类型的指示。因此,他们试图以大约 50% 的成功率(如演示中提到的)推断类型。

从类型提示演示中获取两张重要的幻灯片:
为什么要输入提示?

  • 帮助类型检查器:例如,通过提示您希望对象成为哪种类型,类型检查器可以轻松检测到您是否传递了一个类型不符合预期的对象。
  • 帮助文档:查看您的代码的第三个人将知道预期的内容,因此,如何在不获取它们的情况下使用它TypeErrors .
  • 帮助 IDE 开发更准确、更强大的工具:当知道您的对象是什么类型时,开发环境将更适合建议适当的方法。您可能曾在某个 IDE 中遇到过这种情况,点击 .并弹出未为对象定义的方法/属性。

  • 为什么要使用静态类型检查器?
  • 尽早发现错误 : 这是不言而喻的,我相信。
  • 您的项目越大,您就越需要它 : 再说一遍,有道理。静态语言提供了健壮性和控制
    缺乏动态语言。您的应用程序越大越复杂,控制力和可预测性就越强(从
    行为方面)您需要。
  • 大型团队已经在运行静态分析 : 我猜这验证了前两点。

  • 作为这个简短介绍的结束语 : 这是一个 可选 功能,据我所知,引入它是为了获得静态类型的一些好处。
    你一般不要需要担心它和绝对不需要使用它(特别是在您使用 Python 作为辅助脚本语言的情况下)。它在开发大型项目时应该会有所帮助,因为它提供了急需的稳健性、控制和额外的调试功能。

    使用 mypy 进行类型提示 :
    为了让这个回答更完整,我觉得稍微演示一下比较合适。我将使用 mypy ,启发了类型提示的库,因为它们出现在 PEP 中。这主要是为遇到这个问题并想知道从哪里开始的任何人而写的。
    在我这样做之前,让我重申以下内容:PEP 484不强制执行任何事情;它只是为功能设定一个方向
    的注释和建议指南怎么样可以/应该执行类型检查。你可以注释你的函数和
    提示尽可能多的东西;无论是否存在注释,您的脚本仍将运行,因为 Python 本身不使用它们。
    无论如何,如 PEP 中所述,提示类型通常应采用三种形式:
  • 函数注释 ( PEP 3107 )。
  • 内置/用户模块的 stub 文件。
  • 特价 # type: type补充前两种形式的注释。 (请参阅: What are variable annotations? # type: type 评论的 Python 3.6 更新)

  • 此外,您需要将类型提示与新的 typing 结合使用。 Py3.5 中引入的模块.其中,许多(附加)ABCs (抽象基类)与辅助函数和装饰器一起定义,用于静态检查。大多数 ABC 在 collections.abc包括在内,但以通用形式允许订阅(通过定义 __getitem__() 方法)。
    对于对这些更深入的解释感兴趣的任何人, mypy documentation 写得很好,有很多代码示例演示/描述了他们的检查器的功能;绝对值得一读。
    函数注解和特殊注释:
    首先,观察我们在使用特殊注释时可以获得的一些行为是很有趣的。特价# type: type评论
    如果不能直接推断,可以在变量赋值期间添加以指示对象的类型。简单的任务是
    通常很容易推断,但其他人,如列表(关于其内容),则不能。
    注:如果我们想使用容器的任何派生类并且需要为该容器指定内容,我们 必须使用 通用 类型来自 typing模块。 这些支持索引。
    # Generic List, supports indexing.
    from typing import List
    
    # In this case, the type is easily inferred as type: int.
    i = 0
    
    # Even though the type can be inferred as of type list
    # there is no way to know the contents of this list.
    # By using type: List[str] we indicate we want to use a list of strings.
    a = []  # type: List[str]
    
    # Appending an int to our list
    # is statically not correct.
    a.append(i)
    
    # Appending a string is fine.
    a.append("i")
    
    print(a)  # [0, 'i']
    
    如果我们将这些命令添加到文件中并使用我们的解释器执行它们,一切都会正常运行,并且 print(a)只是打印
    列表内容a . # type评论已被丢弃,被视为没有额外语义含义的普通评论。
    通过使用 mypy 运行它,另一方面,我们得到以下响应:
    (Python3)jimmi@jim: mypy typeHintsCode.py
    typesInline.py:14: error: Argument 1 to "append" of "list" has incompatible type "int"; expected "str"
    
    表示str的列表对象不能包含 int ,从静态上讲,它是健全的。这可以通过遵守 a 的类型来解决。并且只附加 str对象或通过更改 a 的内容类型表示任何值都是可以接受的(在从 List[Any] 导入 Any 后使用 typing 直观地执行)。
    函数注解以 param_name : type 的形式添加在函数签名中的每个参数和返回类型之后使用 -> type 指定结束函数冒号前的符号;所有注释都存储在 __annotations__ 中以方便的字典形式显示该函数的属性。使用一个简单的例子(不需要来自 typing 模块的额外类型):
    def annotated(x: int, y: str) -> bool:
        return x < y
    
    annotated.__annotations__属性现在具有以下值:
    {'y': <class 'str'>, 'return': <class 'bool'>, 'x': <class 'int'>}
    
    如果我们是一个完全的新手,或者我们熟悉 Python 2.7 概念,因此不知道 TypeError潜伏在对比annotated ,我们可以再进行一次静态检查,捕获错误并省去一些麻烦:
    (Python3)jimmi@jim: mypy typeHintsCode.py
    typeFunction.py: note: In function "annotated":
    typeFunction.py:2: error: Unsupported operand types for > ("str" and "int")
    
    除其他外,使用无效参数调用函数也会被捕获:
    annotated(20, 20)
    
    # mypy complains:
    typeHintsCode.py:4: error: Argument 2 to "annotated" has incompatible type "int"; expected "str"
    
    这些基本上可以扩展到任何用例,并且捕获的错误比基本调用和操作扩展得更远。你的类型
    can check for 真的很灵活,我只是给了它潜力的一个小高峰。看看 typing模块,
    PEP 或 mypy文档将使您更全面地了解所提供的功能。
    stub 文件:
    stub 文件可用于两种不同的非互斥情况:
  • 您需要键入检查一个不想直接更改其函数签名的模块
  • 您想编写模块并进行类型检查,但还想将注释与内容分开。

  • stub 文件(扩展名为 .pyi )是您正在制作/想要使用的模块的带注释的接口(interface)。它们包含
    您要使用丢弃的函数体进行类型检查的函数的签名。为了感受这一点,给定一个集合
    名为 randfunc.py 的模块中的三个随机函数:
    def message(s):
        print(s)
    
    def alterContents(myIterable):
        return [i for i in myIterable if i % 2 == 0]
    
    def combine(messageFunc, itFunc):
        messageFunc("Printing the Iterable")
        a = alterContents(range(1, 20))
        return set(a)
    
    我们可以创建一个 stub 文件 randfunc.pyi ,如果我们愿意,我们可以在其中设置一些限制。缺点是
    在试图理解应该是什么时,没有 stub 查看源代码的人将无法真正获得注释帮助
    要经过哪里。
    无论如何, stub 文件的结构非常简单:添加所有具有空体的函数定义(pass 填充)和
    根据您的要求提供注释。在这里,假设我们只想使用 int我们容器的类型。
    # Stub for randfucn.py
    from typing import Iterable, List, Set, Callable
    
    def message(s: str) -> None: pass
    
    def alterContents(myIterable: Iterable[int])-> List[int]: pass
    
    def combine(
        messageFunc: Callable[[str], Any],
        itFunc: Callable[[Iterable[int]], List[int]]
    )-> Set[int]: pass
    
    combine函数给出了为什么你可能想要在不同的文件中使用注释的指示,它们有时会困惑
    代码并降低可读性(Python 的大禁忌)。你当然可以使用类型别名,但有时比它更令人困惑
    帮助(所以明智地使用它们)。

    这应该会让您熟悉 Python 中类型提示的基本概念。即使使用的类型检查器已经mypy你应该逐渐开始看到更多的弹出窗口,一些在 IDE 内部( PyCharm ,)和其他作为标准 Python 模块。
    当我找到它们(或如果建议)时,我将尝试在以下列表中添加其他检查器/相关包。
    我知道的跳棋 :
  • Mypy : 如此处所述。
  • PyType : 谷歌,使用与我收集的不同的符号,可能值得一看。

  • 相关包/项目 :
  • typeshed:官方 Python 存储库包含各种标准库的 stub 文件。
  • typeshed project 实际上是您可以查看如何在您自己的项目中使用类型提示的最佳场所之一。我们以the __init__ dunders of the Counter class为例在相应的 .pyi文件:
    class Counter(Dict[_T, int], Generic[_T]):
            @overload
            def __init__(self) -> None: ...
            @overload
            def __init__(self, Mapping: Mapping[_T, int]) -> None: ...
            @overload
            def __init__(self, iterable: Iterable[_T]) -> None: ...
    
    Where _T = TypeVar('_T') is used to define generic classes .对于 Counter我们可以看到它可以在其初始化器中不带任何参数,获得单个 Mapping从任何类型到 int或拨打 Iterable任何类型。

    通知 :我忘记提及的一件事是typing模块是临时引入的。来自 PEP 411 :

    A provisional package may have its API modified prior to "graduating" into a "stable" state. On one hand, this state provides the package with the benefits of being formally part of the Python distribution. On the other hand, the core development team explicitly states that no promises are made with regards to the the stability of the package's API, which may change for the next release. While it is considered an unlikely outcome, such packages may even be removed from the standard library without a deprecation period if the concerns regarding their API or maintenance prove well-founded.


    所以拿一撮盐来这里;我怀疑它是否会被删除或以重要的方式改变,但人们永远无法知道。

    ** 完全是另一个主题,但在类型提示范围内有效: PEP 526 : Syntax for Variable Annotations是努力取代# type通过引入新语法来注释,允许用户以简单的方式注释变量类型 varname: type声明。
    What are variable annotations? ,如前所述,对这些进行简要介绍。

    关于python - Python 3.5 中的类型提示是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32557920/

    相关文章:

    python - 灵活的数据库模型,供用户在 Django 中为数据库表定义额外的列

    python - 如何在pytorch模型中加载检查点文件?

    python - Python 如何在内部存储列表?

    python-3.x - Python 参数化格式化

    python - 如何创建 5 个新列来反射(reflect)每个客户过去的 5 笔交易?

    python - Scrapy 返回/传递数据到另一个模块

    python - ImportError : libX11. so.6: 无法打开共享对象文件: 没有这样的文件或目录 & 权限也被拒绝

    python - 如何将 JSON 数据格式化为包含逗号?

    python - Django 错误 - django.db.utils.DatabaseError : Data truncated for column 'applied' at row 1

    python - numpy dot() 和 Python 3.5+ 矩阵乘法之间的区别@