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

发布于 2025-01-22 19:37:36 字数 256 浏览 0 评论 0 原文

Python 3.5中最容易说的功能之一是类型提示

类型提示的示例“ http://lwn.net/articles/640359/” rel =“ noreferrer”>这个同时还提到要负责任地使用类型提示。有人可以解释更多有关它们的信息,什么时候应该使用它们,何时不使用它们?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

楠木可依 2025-01-29 19:37:36

我建议阅读 pep 483 pep 484 并观看此介绍 guido type minting。

简而言之类型提示实际上是单词的含义。您暗示您使用的对象的类型

由于python的动态性质,推断或检查所使用的对象的类型尤其困难。这个事实使开发人员很难理解他们尚未编写的代码中的确切情况,最重要的是,对于许多IDE中找到的类型检查工具( pycharm


要从类型提示演示文稿中获取两个重要幻灯片:

为什么键入提示

  1. ?成为类型的检查器可以轻松检测,例如,您是否正在传递带有预期类型的​​对象。
  2. 有助于文档:查看您的代码的第三个人将知道什么在何处,ergo,如何使用它而不获得它们 typeerrors
  3. 帮助IDE开发更准确,更强大的工具:开发环境将在知道您的对象是哪种类型时更适合建议适当的方法。您可能已经在某个时候使用一些IDE经历了这一点,击中,并弹出了未针对对象定义的方法/属性。

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

  • 更早地找到错误:这是不言而喻的。
  • 您的项目越大,您需要它:同样,有意义。静态语言提供了强大的控制
    动态语言缺乏。您的应用程序越大,越复杂,则变得越多(从
    您需要的行为方面)。
  • 大型团队已经在运行静态分析:我猜这可以验证前两个点。

作为此小简介的闭幕说明:这是可选功能,据我了解,它是为了获得静态键入的一些好处而引入的。 。

通常,您 不需要担心它,肯定不需要使用它(尤其是在使用Python作为辅助脚本语言的情况下)。当开发大型项目时,它应该有帮助,它提供了急需的鲁棒性,控制和其他调试功能。


类型提示使用mypy

为了使这个答案更加完整,我认为一些演示是合适的。我将使用 mypy ,启发类型提示的库是呈现的。在佩普。这主要是为任何遇到这个问题并想知道从哪里开始的人而写的。

在这样做之前,让我重申以下内容: pep 484 任何事物;它只是为功能设定方向
应为的注释和提出指南类型检查应该/应执行。您可以注释您的功能,
提示您想要的尽可能多的东西;无论注释的存在如何,您的脚本仍将运行,因为Python本身不使用它们。

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

,您还需要与新的 键入 py> py> py3.5 中引入的模块。在其中,许多(额外) abcs (摘要基础类)带有辅助功能和装饰器,用于静态检查。包括 collections.abc 中的大多数ABC,但以a generic 表单为允许订阅(通过定义 __ getItem __()方法)。

对于对这些更深入的解释感兴趣的任何人,写得很好,并且有很多代码样本演示/描述其检查器的功能;绝对值得一读。

功能注释和特殊评论:

首先,观察使用特殊评论时可以得到的某些行为很有趣。特殊#类型:类型评论
可以在变量分配期间添加,以指示如果不能直接推断对象的类型。简单的作业是
通常很容易推断,但其他人,例如清单(关于其内容),不能。

注意:如果我们想使用容器的任何衍生物 ,并且需要指定该容器的内容,我们必须 通用 类型键入模块。 这些支持索引。

# 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 的内容。 #类型注释已被丢弃,被视为没有其他语义含义的普通注释。

另一方面,通过使用 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 [任何] 在直觉上执行任何已从键入中导入任何)。

函数注释以 param_name形式添加:type 在您的函数签名中的每个参数和使用 - &gt指定返回类型之后; 在结束功能结肠之前符号;所有注释都存储在 __注释__ 该功能的属性中,以方便的字典形式。 不需要键入模块中的额外类型):

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

注释。

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

使用一个琐碎的示例( ,或者我们熟悉Python&nbsp; 2.7概念,因此不知道 typeError 潜伏在注释>注释的比较中,我们可以执行另一个静态检查,捕获错误并保存。我们有一些麻烦:

(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"

这些功能可以扩展到任何用例,而所捕获的错误比基本的呼叫和操作更远。您的类型
可以检查真的很灵活,我只是赋予其潜力的小潜行峰。在键入模块中查看
PEPS或 mypy 文档将使您对所提供的功能有更全面的想法。

存根文件:

存根文件可以在两种不同的非相互排斥的情况下使用:

  • 您需要键入一个模块,该模块不想直接更改
  • 要编写模块并具有类型检查的功能签名,但还需要分开来自内容的注释。

存根文件(带有 .pyi 的扩展名)是您要/要使用的模块的注释接口。它们包含
您想通过丢弃功能的主体来键入功能的功能的签名。为了感觉到这一点
在名为 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 ,如果我们希望这样做,我们可以放置一些限制。不利的一面是
当试图理解所假定的内容时
通过的地方。

无论如何,存根文件的结构非常简单:用空物体添加所有功能定义( 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模块。

我将尝试在下面的列表中添加其他查看器/相关软件包。

Checkers我知道

相关软件包/项目

  • 打字: 官方Python存储库中容纳了标准库的各种存根文件。

打字项目实际上是您可以寻找的最佳场所之一,以查看如何在自己的项目中使用类型的提示。让我们以示例 ____________________________________ > counter class 的dunders在相应的 .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: ...

其中 _t = typevar('_ t')用于定义通用类。对于 counter 类,我们可以看到它可以在其初始化器中不进行参数,请从任何类型中获取单个映射 em>或以任何类型的 进行


注意:我忘了提到的一件事是,键入模块是在临时基础上引入的。来自 pep 411

临时软件包可能在“毕业”为“稳定”状态之前对其API进行了修改。一方面,该状态为该软件包提供了正式分配的一部分的好处。另一方面,核心开发团队明确指出,对于包装的API的稳定性,没有任何承诺,这可能会改变下一个版本。虽然认为这是不太可能的结果,但如果对其API或维护的疑虑证明,这些包裹甚至可以从不弃用期限的标准库中删除。

因此,用少许盐将东西带到这里;我怀疑它将以重大的方式将其删除或改变,但人们永远不会知道。


** 完全有效,但在类型含义的范围中有效: pep 526 :可变注释的语法是一种努力,通过引入新的新注释来替换#type 注释语法允许用户注释简单 varname中的变量类型:type 语句。

参见 对这些> 进行了较小的介绍。

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

In a nutshell: Type hinting is literally what the words mean. You hint the type of the object(s) you're using.

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:

Why type hints?

  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.

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.


Type hinting with mypy:

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.

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.

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

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

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

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.

Function annotations and special comments:

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.

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']

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.

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"

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

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

The annotated.__annotations__ attribute now has the following values:

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

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"

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:

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

  • You need to type check a module for which you do not want to directly alter the function signatures
  • You want to write modules and have type-checking but additionally want to separate annotations from content.

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)

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.

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

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


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

Checkers I know of:

  • Mypy: as described here.
  • PyType: By Google, uses different notation from what I gather, probably worth a look.

Related Packages/Projects:

  • typeshed: Official Python repository housing an assortment of stub files for the standard library.

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

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.


Notice: One thing I forgot to mention was that the typing module has been introduced on a provisional basis. From 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.

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.


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

半透明的墙 2025-01-29 19:37:36

添加到吉姆的精心响应答案

检查 键入 module - 此模块支持类型提示,如 pep 484

例如,以下功能获取并返回类型 str 的值,并注释如下:

def greeting(name: str) -> str:
    return 'Hello ' + name

键入模块还支持:

  1. type aleiasing
  2. 键入回调函数
  3. generics - 已经扩展了抽象基础类以支持订阅以表示预期的类型用于容器元素。
  4. 用户定义的通用类型定义的类可以定义为通用类。
  5. 任何类型 - 每种类型都是任何类型的子类型。

Adding to Jim's elaborate answer:

Check the typing module -- this module supports type hints as specified by PEP 484.

For example, the function below takes and returns values of type str and is annotated as follows:

def greeting(name: str) -> str:
    return 'Hello ' + name

The typing module also supports:

  1. Type aliasing.
  2. Type hinting for callback functions.
  3. Generics - Abstract base classes have been extended to support subscription to denote expected types for container elements.
  4. User-defined generic types - A user-defined class can be defined as a generic class.
  5. Any type - Every type is a subtype of Any.
红衣飘飘貌似仙 2025-01-29 19:37:36

新发布的Pycharm 5支持类型提示。在他们有关它的博客文章中(请参阅 python 3.5在Pycharm 5 中暗示)他们提供了一个很好的解释哪种类型的提示是和不是,以及有关如何在代码中使用它们的几个示例和插图。

此外,它在python 2.7中得到了支持,如此评论

Pycharm支持Python 2.7,Python 3.2-3.4的PYPI的打字模块。 对于2.7,您必须在 *.pyi存根文件中添加类型提示,因为函数注释在Python 3.0 中添加了。。

The newly released PyCharm 5 supports type hinting. In their blog post about it (see Python 3.5 type hinting in PyCharm 5) they offer a great explanation of what type hints are and aren't along with several examples and illustrations for how to use them in your code.

Additionally, it is supported in Python 2.7, as explained in this comment:

PyCharm supports the typing module from PyPI for Python 2.7, Python 3.2-3.4. For 2.7 you have to put type hints in *.pyi stub files since function annotations were added in Python 3.0.

匿名的好友 2025-01-29 19:37:36

类型提示是用于可维护性的,并且不会被Python解释。在下面的代码中,行 def add(self,ic:int)不会导致错误,直到下一个返回... line:

class C1:
    def __init__(self):
        self.idn = 1
    def add(self, ic: int):
        return self.idn + ic

c1 = C1()
c1.add(2)

c1.add(c1)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 5, in add
TypeError: unsupported operand type(s) for +: 'int' and 'C1'

Type hints are for maintainability and don't get interpreted by Python. In the code below, the line def add(self, ic:int) doesn't result in an error until the next return... line:

class C1:
    def __init__(self):
        self.idn = 1
    def add(self, ic: int):
        return self.idn + ic

c1 = C1()
c1.add(2)

c1.add(c1)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 5, in add
TypeError: unsupported operand type(s) for +: 'int' and 'C1'
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文