Python 3.5 中的类型提示是什么? [英] What are type hints in Python 3.5?

查看:42
本文介绍了Python 3.5 中的类型提示是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Python 3.5 中最受关注的功能之一是类型提示.

One of the most talked-about features in Python 3.5 is type hints.

这篇文章这个 同时还提到负责任地使用类型提示.有人可以解释更多关于它们的信息,以及何时应该使用,何时不使用?

An example of type hints is mentioned in this article and this one while also mentioning to use type hints responsibly. Can someone explain more about them and when they should be used and when not?

推荐答案

我建议阅读 PEP 483PEP 484 并观看 本演示文稿 来自 Guido 关于类型提示.

I would suggest reading PEP 483 and PEP 484 and watching this presentation by Guido on type hinting.

简而言之:类型提示就是字面上的意思.您暗示了您正在使用的对象的类型.

由于 Python 的动态性质,推断或检查正在使用的对象的类型特别困难.这一事实使开发人员难以理解他们尚未编写的代码究竟发生了什么,最重要的是,对于许多 IDE 中的类型检查工具(PyCharmPyDev 浮现在脑海中)由于它们没有任何关于对象类型的指示符,因此受到限制.因此,他们试图以大约 50% 的成功率(如演示中提到的)推断类型.

Due to the dynamic nature of Python, inferring or checking the type of an object being used is especially hard. This fact makes it hard for developers to understand what exactly is going on in code they haven't written and, most importantly, for type checking tools found in many IDEs (PyCharm and PyDev come to mind) that are limited due to the fact that they don't have any indicator of what type the objects are. As a result they resort to trying to infer the type with (as mentioned in the presentation) around 50% success rate.

从类型提示演示中获取两张重要的幻灯片:

To take two important slides from the type hinting presentation:

  1. 帮助类型检查器:通过暗示您希望对象是什么类型,类型检查器可以轻松检测例如,您传递的对象是否具有非预期的类型.
  2. 帮助处理文档:查看您的代码的第三方将知道预期的内容,因此,如何使用它而不会获得TypeErrors.
  3. 帮助 IDE 开发更准确、更强大的工具:当知道您的对象是什么类型时,开发环境将更适合建议适当的方法.您可能曾在某个 IDE 中遇到过这种情况,点击 . 并弹出未为对象定义的方法/属性.
  1. Helps type checkers: By hinting at what type you want the object to be the type checker can easily detect if, for instance, you're passing an object with a type that isn't expected.
  2. Helps with documentation: A third person viewing your code will know what is expected where, ergo, how to use it without getting them TypeErrors.
  3. Helps IDEs develop more accurate and robust tools: Development Environments will be better suited at suggesting appropriate methods when know what type your object is. You have probably experienced this with some IDE at some point, hitting the . and having methods/attributes pop up which aren't defined for an object.

为什么要使用静态类型检查器?

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

    • Find bugs sooner: This is self-evident, I believe.
    • The larger your project the more you need it: Again, makes sense. Static languages offer a robustness and control that dynamic languages lack. The bigger and more complex your application becomes the more control and predictability (from a behavioral aspect) you require.
    • Large teams are already running static analysis: I'm guessing this verifies the first two points.
    • 作为这个简短介绍的结束语:这是一个可选功能,据我所知,引入它是为了获得一些好处静态类型.

      As a closing note for this small introduction: This is an optional feature and, from what I understand, it has been introduced in order to reap some of the benefits of static typing.

      您通常不必担心它并且绝对不需要使用它(尤其是在您使用 Python 作为辅助脚本语言的情况下).它在开发大型项目时应该会很有帮助,因为它提供了急需的稳健性、控制和额外的调试功能.

      You generally do not need to worry about it and definitely don't need to use it (especially in cases where you use Python as an auxiliary scripting language). It should be helpful when developing large projects as it offers much needed robustness, control and additional debugging capabilities.

      为了让这个回答更完整,我觉得稍微演示一下比较合适.我将使用 mypy,该库在呈现类型提示时激发了它们的灵感在 PEP 中.这主要是写给任何遇到这个问题并想知道从哪里开始的人.

      In order to make this answer more complete, I think a little demonstration would be suitable. I'll be using mypy, the library which inspired Type Hints as they are presented in the PEP. This is mainly written for anybody bumping into this question and wondering where to begin.

      在我这样做之前,让我重申以下几点:PEP 484'不强制执行任何事情;它只是为功能设定一个方向关于如何类型检查可以/应该执行的注释和建议指南.你可以注释你的函数和提示尽可能多的东西;无论是否存在注释,您的脚本仍会运行,因为 Python 本身不使用它们.

      Before I do that let me reiterate the following: PEP 484 doesn't enforce anything; it is simply setting a direction for function annotations and proposing guidelines for how type checking can/should be performed. You can annotate your functions and hint as many things as you want; your scripts will still run regardless of the presence of annotations because Python itself doesn't use them.

      无论如何,如 PEP 中所述,提示类型通常应采用三种形式:

      Anyways, as noted in the PEP, hinting types should generally take three forms:

      • 函数注释(PEP 3107).
      • 内置/用户模块的存根文件.
      • 特殊的 # type: type 对前两种形式进行补充的注释.(请参阅:什么是变量注释? 用于 # type: type 注释的 Python 3.6 更新
      • Function annotations (PEP 3107).
      • Stub files for built-in/user modules.
      • Special # type: type comments that complement the first two forms. (See: What are variable annotations? for a Python 3.6 update for # type: type comments)

      此外,您需要将类型提示与新的 结合使用Py3.5 中引入的typing 模块.其中,许多(附加)ABCs(抽象基类)被定义为带有辅助函数和装饰器,用于静态检查.collections.abc 中的大多数 ABC 都包含在内,但以 通用 形式包含以允许订阅(通过定义 __getitem__() 方法).

      Additionally, you'll want to use type hints in conjunction with the new typing module introduced in Py3.5. In it, many (additional) ABCs (abstract base classes) are defined along with helper functions and decorators for use in static checking. Most ABCs in collections.abc are included, but in a generic form in order to allow subscription (by defining a __getitem__() method).

      对于对这些更深入的解释感兴趣的任何人,mypy 文档 写得非常好,有很多代码示例演示/描述了检查器的功能;绝对值得一读.

      For anyone interested in a more in-depth explanation of these, the mypy documentation is written very nicely and has a lot of code samples demonstrating/describing the functionality of their checker; it is definitely worth a read.

      首先,观察我们在使用特殊注释时可以获得的一些行为很有趣.特殊的 # type: type 注释如果不能直接推断,可以在变量赋值期间添加以指示对象的类型.简单的任务是通常很容易推断,但其他人,如列表(关于其内容),则不能.

      First, it's interesting to observe some of the behavior we can get when using special comments. Special # type: type comments can be added during variable assignments to indicate the type of an object if one cannot be directly inferred. Simple assignments are generally easily inferred but others, like lists (with regard to their contents), cannot.

      注意:如果我们想使用容器的任何派生词并且需要为那个容器指定内容,我们必须使用typing 模块中的 >generic 类型.这些支持索引.

      Note: If we want to use any derivative of containers and need to specify the contents for that container we must use the generic types from the typing module. These support indexing.

      # 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 注释已被丢弃,被视为没有附加语义含义的普通注释.

      If we add these commands to a file and execute them with our interpreter, everything works just fine and print(a) just prints the contents of list a. The # type comments have been discarded, treated as plain comments which have no additional semantic meaning.

      另一方面,通过使用 mypy 运行它,我们得到以下响应:

      By running this with mypy, on the other hand, we get the following response:

      (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 的内容的类型来解决任何值都是可以接受的(在从 typing 导入 Any 之后,使用 List[Any] 直观地执行).

      Indicating that a list of str objects cannot contain an int, which, statically speaking, is sound. This can be fixed by either abiding to the type of a and only appending str objects or by changing the type of the contents of a to indicate that any value is acceptable (Intuitively performed with List[Any] after Any has been imported from typing).

      函数注释以 param_name : type 形式添加在函数签名中的每个参数之后,并使用 -> 指定返回类型.在结束函数冒号前键入符号;所有注释都以方便的字典形式存储在该函数的 __annotations__ 属性中.使用一个简单的示例(不需要 typing 模块中的额外类型):

      Function annotations are added in the form param_name : type after each parameter in your function signature and a return type is specified using the -> type notation before the ending function colon; all annotations are stored in the __annotations__ attribute for that function in a handy dictionary form. Using a trivial example (which doesn't require extra types from the typing module):

      def annotated(x: int, y: str) -> bool:
          return x < y
      

      annotated.__annotations__ 属性现在具有以下值:

      The annotated.__annotations__ attribute now has the following values:

      {'y': <class 'str'>, 'return': <class 'bool'>, 'x': <class 'int'>}
      

      如果我们是一个完全的新手,或者我们熟悉 Python 2.7 概念并且因此不知道潜伏在 annotated 比较中的 TypeError,我们可以执行另一个静态检查,捕获错误并为我们节省一些麻烦:

      If we're a complete newbie, or we are familiar with Python 2.7 concepts and are consequently unaware of the TypeError lurking in the comparison of annotated, we can perform another static check, catch the error and save us some trouble:

      (Python3)jimmi@jim: mypy typeHintsCode.py
      typeFunction.py: note: In function "annotated":
      typeFunction.py:2: error: Unsupported operand types for > ("str" and "int")
      

      除此之外,调用带有无效参数的函数也会被捕获:

      Among other things, calling the function with invalid arguments will also get caught:

      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 文档会让您更全面地了解所提供的功能.

      These can be extended to basically any use case and the errors caught extend further than basic calls and operations. The types you can check for are really flexible and I have merely given a small sneak peak of its potential. A look in the typing module, the PEPs or the mypy documentation will give you a more comprehensive idea of the capabilities offered.

      存根文件可用于两种不同的非互斥情况:

      Stub files can be used in two different non mutually exclusive cases:

      • 您需要对不想直接更改函数签名的模块进行类型检查
      • 您想编写模块并进行类型检查,但还想将注释与内容分开.

      存根文件(扩展名为 .pyi)是您正在制作/想要使用的模块的带注释的接口.他们包含您要使用丢弃的函数体进行类型检查的函数的签名.为了感受这一点,给定一个集合randfunc.py 模块中的三个随机函数:

      What stub files (with an extension of .pyi) are is an annotated interface of the module you are making/want to use. They contain the signatures of the functions you want to type-check with the body of the functions discarded. To get a feel of this, given a set of three random functions in a module named 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)
      

      我们可以创建一个存根文件randfunc.pyi,如果我们愿意,我们可以在其中设置一些限制.缺点是在试图理解应该是什么时,没有存根查看源代码的人将无法真正获得注释帮助从哪里通过.

      We can create a stub file randfunc.pyi, in which we can place some restrictions if we wish to do so. The downside is that somebody viewing the source without the stub won't really get that annotation assistance when trying to understand what is supposed to be passed where.

      无论如何,存根文件的结构非常简单:添加所有具有空体的函数定义(pass 填充)和根据您的要求提供注释.在这里,假设我们只想为容器使用 int 类型.

      Anyway, the structure of a stub file is pretty simplistic: Add all function definitions with empty bodies (pass filled) and supply the annotations based on your requirements. Here, let's assume we only want to work with int types for our Containers.

      # 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 的大禁忌).你当然可以使用类型别名,但有时比它更令人困惑帮助(所以明智地使用它们).

      The combine function gives an indication of why you might want to use annotations in a different file, they some times clutter up the code and reduce readability (big no-no for Python). You could of course use type aliases but that sometime confuses more than it helps (so use them wisely).

      这应该会让您熟悉 Python 中类型提示的基本概念.即使使用的类型检查器已经mypy 你应该逐渐开始看到更多的弹出窗口,一些在 IDE 内部(PyCharm,) 和其他标准 Python 模块.

      This should get you familiarized with the basic concepts of type hints in Python. Even though the type checker used has been mypy you should gradually start to see more of them pop-up, some internally in IDEs (PyCharm,) and others as standard Python modules.

      我会尝试在以下列表中添加其他检查器/相关包,如果我找到它们(或如果建议).

      I'll try and add additional checkers/related packages in the following list when and if I find them (or if suggested).

      我知道的跳棋:

      • Mypy:如此处所述.
      • PyType:由 Google 提供,使用与我收集的不同的符号,可能值得一看.

      相关包/项目:

      • typeshed: 官方 Python 存储库,其中包含各种存根文件,用于标准库.

      typeshed 项目实际上是您可以查看如何在您自己的项目中使用类型提示的最佳场所之一.我们以__init__ 对应.pyi文件中Counter的dunders:

      The typeshed project is actually one of the best places you can look to see how type hinting might be used in a project of your own. Let's take as an example the __init__ dunders of the Counter class in the corresponding .pyi file:

      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: ...
      

      其中 _T = TypeVar('_T') 用于定义泛型类.对于 Counter 类,我们可以看到它可以在其初始值设定项中不带任何参数,也可以从任何类型到 int 获取单个 Mappingem>或采用任何类型的Iterable.

      Where _T = TypeVar('_T') is used to define generic classes. For the Counter class we can see that it can either take no arguments in its initializer, get a single Mapping from any type to an int or take an Iterable of any type.

      注意:我忘记提及的一件事是typing 模块已经临时引入.来自 PEP 411:

      Notice: One thing I forgot to mention was that the typing module has been introduced on a provisional basis. From PEP 411:

      临时软件包可能会在毕业"之前修改其 API进入稳定"状态状态.一方面,这种状态为包提供了正式成为 Python 发行版一部分的好处.另一方面,核心开发团队明确表示不承诺包的 API 的稳定性,这可能会在下一个版本中发生变化.虽然这被认为是不太可能的结果,但如果对其 API 或维护的担忧被证明是有根据的,这些包甚至可能会在没有弃用期的情况下从标准库中删除.

      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.

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

      So take things here with a pinch of salt; I'm doubtful it will be removed or altered in significant ways, but one can never know.

      ** 完全是另一个主题,但在类型提示范围内有效:PEP 526:变量注释的语法 是为了替换 # type 注释通过引入新的语法,允许用户在简单的 varname: type 语句中注释变量的类型.

      ** Another topic altogether, but valid in the scope of type-hints: PEP 526: Syntax for Variable Annotations is an effort to replace # type comments by introducing new syntax which allows users to annotate the type of variables in simple varname: type statements.

      参见什么是变量注释?,如前所述,对这些内容进行简要介绍.

      See What are variable annotations?, as previously mentioned, for a small introduction to these.

      这篇关于Python 3.5 中的类型提示是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆