python 学习指南_Python类型检查终极指南

python 学习指南

In this guide, you will get a look into Python type checking. Traditionally, types have been handled by the Python interpreter in a flexible but implicit way. Recent versions of Python allow you to specify explicit type hints that can be used by different tools to help you develop your code more efficiently.

在本指南中,您将了解Python类型检查。 传统上,类型由Python解释器以灵活但隐式的方式处理。 Python的最新版本允许您指定显式类型提示,不同的工具可以使用这些类型提示来帮助您更有效地开发代码。

In this tutorial, you’ll learn about the following:

在本教程中,您将了解以下内容:

  • Type annotations and type hints
  • Adding static types to code, both your code and the code of others
  • Running a static type checker
  • Enforcing types at runtime
  • 类型注释和类型提示
  • 在代码中添加静态类型,包括您的代码和其他代码
  • 运行静态类型检查器
  • 在运行时强制类型

This is a comprehensive guide that will cover a lot of ground. If you want to just get a quick glimpse of how type hints work in Python, and see whether type checking is something you would include in your code, you don’t need to read all of it. The two sections Hello Types and Pros and Cons will give you a taste of how type checking works and recommendations about when it’ll be useful.

这是一本全面的指南,内容涉及很多领域。 如果您想快速了解Python中类型提示的工作原理,并查看是否将类型检查包含在代码中,则无需阅读所有内容。 Hello TypesPros and Cons这两个部分将带您领略类型检查的工作原理以及有关何时使用的建议。

Free Bonus: 5 Thoughts On Python Mastery, a free course for Python developers that shows you the roadmap and the mindset you’ll need to take your Python skills to the next level.

免费奖金: 关于Python精通的5个想法 ,这是针对Python开发人员的免费课程,向您展示了将Python技能提升到新水平所需的路线图和心态。

类型系统 (Type Systems)

All programming languages include some kind of type system that formalizes which categories of objects it can work with and how those categories are treated. For instance, a type system can define a numerical type, with 42 as one example of an object of numerical type.

所有编程语言都包括某种类型系统 ,该系统形式化了可以使用的对象类别以及如何对待这些类别。 例如,类型系统可以定义数字类型,其中42是数字类型对象的一个​​示例。

动态打字 (Dynamic Typing)

Python is a dynamically typed language. This means that the Python interpreter does type checking only as code runs, and that the type of a variable is allowed to change over its lifetime. The following dummy examples demonstrate that Python has dynamic typing:

Python是一种动态类型的语言。 这意味着Python解释器仅在代码运行时才进行类型检查,并且允许变量的类型在其生命周期内进行更改。 以下虚拟示例演示Python具有动态类型:

>>>
>>> if False:
...     1 + "two"  # This line never runs, so no TypeError is raised
... else:
...     1 + 2
...
3

>>> 1 + "two"  # Now this is type checked, and a TypeError is raised
TypeError: unsupported operand type(s) for +: 'int' and 'str'

>>>

In the first example, the branch 1 + "two" never runs so it’s never type checked. The second example shows that when 1 + "two" is evaluated it raises a TypeError since you can’t add an integer and a string in Python.

在第一个示例中,分支1 + "two"从不运行,因此永远不会经过类型检查。 第二个示例显示,当对1 + "two"求值时,它将引发TypeError因为您无法在Python中添加整数和字符串。

Next, let’s see if variables can change type:

接下来,让我们看看变量是否可以更改类型:

>>>
>>> thing = "Hello"
>>> type(thing)
<class 'str'>

>>> thing = 28.1
>>> type(thing)
<class 'float'>

>>>

type() returns the type of an object. These examples confirm that the type of thing is allowed to change, and Python correctly infers the type as it changes.

type()返回对象的类型。 这些示例确认thing的类型被允许更改,并且Python在更改类型时正确地推断出该类型。

静态打字 (Static Typing)

The opposite of dynamic typing is static typing. Static type checks are performed without running the program. In most statically typed languages, for instance C and Java, this is done as your program is compiled.

动态类型化的反面是静态类型化。 在不运行程序的情况下执行静态类型检查。 在大多数静态类型的语言中,例如C和Java,这是在编译程序时完成的。

With static typing, variables generally are not allowed to change types, although mechanisms for casting a variable to a different type may exist.

对于静态类型,尽管可能存在将变量转换为其他类型的机制,但是通常不允许变量更改类型。

Let’s look at a quick example from a statically typed language. Consider the following Java snippet:

让我们看一个来自静态类型语言的简单示例。 考虑以下Java代码段:

 String String thingthing ;
;
thing thing = = "Hello""Hello" ;
;

The first line declares that the variable name thing is bound to the String type at compile time. The name can never be rebound to another type. In the second line, thing is assigned a value. It can never be assigned a value that is not a String object. For instance, if you were to later say thing = 28.1f the compiler would raise an error because of incompatible types.

第一行声明在编译时将变量名thing绑定到String类型。 这个名字永远不会反弹到另一种类型。 在第二行中,为thing分配了一个值。 永远不能为它分配一个非String对象的值。 例如,如果您稍后要说thing = 28.1f则编译器会由于类型不兼容而引发错误。

Python will always remain a dynamically typed language. However, PEP 484 introduced type hints, which make it possible to also do static type checking of Python code.

Python将始终是一种动态类型化的语言 。 但是, PEP 484引入了类型提示,这使得还可以对Python代码进行静态类型检查。

Unlike how types work in most other statically typed languages, type hints by themselves don’t cause Python to enforce types. As the name says, type hints just suggest types. There are other tools, which you’ll see later, that perform static type checking using type hints.

与大多数其他静态类型语言中的类型工作方式不同,类型提示本身不会导致Python强制执行类型。 顾名思义,类型提示只是建议类型。 还有其他工具, 您将在以后看到 ,它们使用类型提示执行静态类型检查。

鸭打字 (Duck Typing)

Another term that is often used when talking about Python is duck typing. This moniker comes from the phrase “if it walks like a duck and it quacks like a duck, then it must be a duck” (or any of its variations).

在谈论Python时经常使用的另一个术语是鸭子类型 。 这个绰号来自“如果它像鸭子一样走路,而像鸭子一样嘎嘎叫,那么它一定是鸭子”(或其任何变体 )。

Duck typing is a concept related to dynamic typing, where the type or the class of an object is less important than the methods it defines. Using duck typing you do not check types at all. Instead you check for the presence of a given method or attribute.

鸭子类型是与动态类型相关的概念,其中对象的类型或类不如其定义的方法重要。 使用鸭子类型您根本不需要检查类型。 而是检查给定方法或属性的存在。

As an example, you can call len() on any Python object that defines a .__len__() method:

例如,您可以在定义.__len__()方法的任何Python对象上调用len()

>>>
>>>
 >>>  class TheHobbit :
...     def __len__ ( self ):
...         return 95022
...
>>>  the_hobbit = TheHobbit ()
>>>  len ( the_hobbit )
95022

Note that the call to len() gives the return value of the .__len__() method. In fact, the implementation of len() is essentially equivalent to the following:

请注意,对len()的调用给出了.__len__()方法的返回值。 实际上, len()的实现基本上等同于以下内容:

In order to call len(obj), the only real constraint on obj is that it must define a .__len__() method. Otherwise, the object can be of types as different as str, list, dict, or TheHobbit.

为了调用len(obj) ,对obj的唯一真正限制是它必须定义.__len__()方法。 否则,对象的类型可以与strlistdictTheHobbit

Duck typing is somewhat supported when doing static type checking of Python code, using structural subtyping. You’ll learn more about duck typing later.

使用结构子类型对Python代码进行静态类型检查时,在某种程度上支持鸭子类型 。 稍后,您将了解有关鸭类打字的更多信息

你好类型 (Hello Types)

In this section you’ll see how to add type hints to a function. The following function turns a text string into a headline by adding proper capitalization and a decorative line:

在本节中,您将看到如何向函数添加类型提示。 以下函数通过添加适当的大写字母和装饰线将文本字符串转换为标题:

 def def headlineheadline (( texttext , , alignalign == TrueTrue ):
    ):
    if if alignalign :
        :
        return return ff "{text.title()}"{text.title()} nn {'-' * len(text)}"
    {'-' * len(text)}"
    elseelse :
        :
        return return ff " {text.title()} "" {text.title()} " .. centercenter (( 5050 , , "o""o" )
)

By default the function returns the headline left aligned with an underline. By setting the align flag to False you can alternatively have the headline be centered with a surrounding line of o:

默认情况下,该函数返回与下划线对齐的左侧标题。 通过将align标志设置为False您可以使标题以o的环绕线为中心:

>>>
>>>
 >>>  print ( headline ( "python type checking" ))
Python Type Checking
--------------------

>>>  print ( headline ( "python type checking" , align = False ))
oooooooooooooo Python Type Checking oooooooooooooo

It’s time for our first type hints! To add information about types to the function, you simply annotate its arguments and return value as follows:

现在是我们第一类提示的时候了! 要将有关类型的信息添加到函数,只需注释其参数并返回值,如下所示:

The text: str syntax says that the text argument should be of type str. Similarly, the optional align argument should have type bool with the default value True. Finally, the -> str notation specifies that headline() will return a string.

text: str语法表示text参数应为str类型。 同样,可选的align参数的类型应该为bool ,默认值为True 。 最后, -> str表示法指定headline()将返回一个字符串。

In terms of style, PEP 8 recommends the following:

样式方面PEP 8建议以下内容:

  • Use normal rules for colons, that is, no space before and one space after a colon: text: str.
  • Use spaces around the = sign when combining an argument annotation with a default value: align: bool = True.
  • Use spaces around the -> arrow: def headline(...) -> str.
  • 对冒号使用正常规则,即冒号前没有空格,冒号后没有空格: text: str
  • 将参数注释与默认值组合时,请在=号周围使用空格: align: bool = True
  • ->箭头周围使用空格: def headline(...) -> str

Adding type hints like this has no runtime effect: they are only hints and are not enforced on their own. For instance, if we use a wrong type for the (admittedly badly named) align argument, the code still runs without any problems or warnings:

像这样添加类型提示不会对运行时间产生影响:它们只是提示,不能单独执行。 例如,如果我们对align参数使用了错误的类型(公认的错误命名),那么代码仍然可以正常运行,没有任何问题或警告:

>>>
>>> print(headline("python type checking", align="left"))
Python Type Checking
--------------------

>>>

Note: The reason this seemingly works is that the string "left" compares as truthy. Using align="center" would not have the desired effect as "center" is also truthy.

注意:看来有效的原因是字符串"left" 比较为true 。 使用align="center"不会产生预期的效果,因为"center"也是正确的。

To catch this kind of error you can use a static type checker. That is, a tool that checks the types of your code without actually running it in the traditional sense.

要捕获此类错误,可以使用静态类型检查器。 也就是说,该工具可以检查代码的类型,而无需实际运行传统意义上的代码。

You might already have such a type checker built into your editor. For instance PyCharm immediately gives you a warning:

您可能已经在编辑器中内置了这种类型检查器。 例如, PyCharm立即向您发出警告:

PyCharm flagging a type error

The most common tool for doing type checking is Mypy though. You’ll get a short introduction to Mypy in a moment, while you can learn much more about how it works later.

不过,进行类型检查的最常用工具是Mypy 。 稍后,您将对Mypy进行简短介绍,同时稍后可以了解更多有关Mypy的工作原理。

If you don’t already have Mypy on your system, you can install it using pip:

如果您的系统上还没有Mypy,则可以使用pip进行安装:

 $ pip install mypy
$ pip install mypy

Put the following code in a file called headlines.py:

将以下代码放在一个名为headlines.py的文件中:

This is essentially the same code you saw earlier: the definition of headline() and two examples that are using it.

这基本上与您之前看到的代码相同: headline()的定义和两个使用它的示例。

Now run Mypy on this code:

现在在以下代码上运行Mypy:

 $ mypy headlines.py
$ mypy headlines.py
headlines.py:10: error: Argument "align" to "headline" has incompatible
headlines.py:10: error: Argument "align" to "headline" has incompatible
                        type "str"; expected "bool"
                        type "str"; expected "bool"

Based on the type hints, Mypy is able to tell us that we are using the wrong type on line 10.

根据类型提示,Mypy可以告诉我们第10行使用的类型错误。

To fix the issue in the code you should change the value of the align argument you are passing in. You might also rename the align flag to something less confusing:

要解决代码中的问题,您应该更改传入的align参数的值。您还可以将align标志重命名为不太混乱的名称:

Here you’ve changed align to centered, and correctly used a boolean value for centered when calling headline(). The code now passes Mypy:

在这里,您已将align更改为centered ,并在调用headline()时正确使用了一个布尔值来centered 。 现在,代码通过了Mypy:

 $ mypy headlines.py
$ mypy headlines.py
$ 
$ 

No output from Mypy means that no type errors were detected. Furthermore, when you run the code you see the expected output:

Mypy没有输出意味着没有检测到类型错误。 此外,当您运行代码时,您会看到预期的输出:

The first headline is aligned to the left, while the second one is centered.

第一个标题向左对齐,第二个标题居中。

利弊 (Pros and Cons)

The previous section gave you a little taste of what type checking in Python looks like. You also saw an example of one of the advantages of adding types to your code: type hints help catch certain errors. Other advantages include:

上一节向您介绍了Python中的类型检查的外观。 您还看到了一个向代码添加类型的优点之一的示例:类型提示有助于捕获某些错误 。 其他优点包括:

  • Type hints help document your code. Traditionally, you would use docstrings if you wanted to document the expected types of a function’s arguments. This works, but as there is no standard for docstrings (despite PEP 257 they can’t be easily used for automatic checks.

  • Type hints improve IDEs and linters. They make it much easier to statically reason about your code. This in turn allows IDEs to offer better code completion and similar features. With the type annotation, PyCharm knows that text is a string, and can give specific suggestions based on this:

    Code completion in PyCharm on a typed variable

  • Type hints help you build and maintain a cleaner architecture. The act of writing type hints forces you to think about the types in your program. While the dynamic nature of Python is one of its great assets, being conscious about relying on duck typing, overloaded methods, or multiple return types is a good thing.

  • 输入提示可帮助您记录代码 。 传统上,如果要记录函数参数的预期类型,则应使用docstrings 。 这是可行的,但是由于没有文档字符串标准(尽管有PEP 257,它们不能轻易用于自动检查)。

  • 类型提示可改善IDE和linters 。 它们使静态推理代码变得容易得多。 反过来,这使IDE可以提供更好的代码完成和类似功能。 通过类型注释,PyCharm知道text是字符串,并可以基于此给出具体建议:

  • 类型提示可帮助您构建和维护更简洁的体系结构 。 类型提示的行为迫使您考虑程序中的类型。 尽管Python的动态特性是其强大的资产之一,但是意识到依赖于鸭子类型,重载方法或多种返回类型是一件好事。

Of course, static type checking is not all peaches and cream. There are also some downsides you should consider:

当然,静态类型检查并非全部都是桃子和奶油。 您还应考虑以下缺点:

  • Type hints take developer time and effort to add. Even though it probably pays off in spending less time debugging, you will spend more time entering code.

  • Type hints work best in modern Pythons. Annotations were introduced in Python 3.0, and it’s possible to use type comments in Python 2.7. Still, improvements like variable annotations and postponed evaluation of type hints mean that you’ll have a better experience doing type checks using Python 3.6 or even Python 3.7.

  • Type hints introduce a slight penalty in start-up time. If you need to use the typing module the import time may be significant, especially in short scripts.

  • 类型提示需要开发人员花费时间和精力进行添加 。 即使花费较少的调试时间可能会有所回报,但是您将花费更多的时间输入代码。

  • 类型提示在现代Python中效果最好 。 注释是在Python 3.0中引入的,并且可以在Python 2.7中使用类型注释 。 尽管如此,诸如变量注释类型提示的延迟评估之类的改进仍意味着您将拥有使用Python 3.6甚至Python 3.7进行类型检查的更好体验。

  • 类型提示会对启动时间造成轻微的影响 。 如果需要使用typing模块,则导入时间可能很长,尤其是在短脚本中。

You’ll later learn about the typing module, and how it’s necessary in most cases when you add type hints. Importing modules necessarily take some time, but how much?

稍后,您将了解typing模块,以及在大多数情况下添加类型提示时的必要性。 导入模块一定要花一些时间,但是要多少呢?

To get some idea about this, create two files: empty_file.py should be an empty file, while import_typing.py should contain the following line:

为了对此有所了解,请创建两个文件: empty_file.py应该是一个空文件,而import_typing.py应该包含以下行:

 import import typing
typing

On Linux it’s quite easy to check how much time the typing import takes using the perf utility:

在Linux上,使用perf实用程序很容易检查typing需要多少时间:

So running the import typing.py script takes about 45 milliseconds. Of course this is not all time spent on importing typing. Some of this is overhead in starting the Python interpreter, so let’s compare to running Python on an empty file:

因此,运行import typing.py脚本大约需要45毫秒。 当然,这并不是花所有时间在typing 。 其中一些是启动Python解释器的开销,因此让我们与在空文件上运行Python进行比较:

 $ perf stat -r $ perf stat -r 1000 python3.6 empty_file.py

1000 python3.6 empty_file.py

 Performance counter stats for 'python3.6 empty_file.py' (1000 runs):

 Performance counter stats for 'python3.6 empty_file.py' (1000 runs):

 [ ... extra information hidden for brevity ... ]

 [ ... extra information hidden for brevity ... ]

       0.028077845 seconds time elapsed    ( +-  0.49% )
       0.028077845 seconds time elapsed    ( +-  0.49% )

Based on this test, the import of the typing module takes around 17 milliseconds on Python 3.6.

根据此测试,在Python 3.6上, typing模块的导入大约需要17毫秒。

One of the advertised improvements in Python 3.7 is faster startup. Let’s see if the results are different:

Python 3.7中宣传的一项改进是启动速度更快 。 让我们看看结果是否不同:

Indeed, the general startup time is reduced by about 8 milliseconds, and the time to import typing is down from 17 to around 6 milliseconds—almost 3 times faster.

实际上,一般的启动时间减少了大约8毫秒,并且导入typing的时间从17减少到了大约6毫秒-快了将近三倍。

Using timeit

使用timeit

There are similar tools on other platforms. Python itself comes with the timeit module in the standard library. Typically, we would directly use timeit for the timings above. However, timeit struggles to time imports reliably because Python is clever about importing modules only once. Consider the following example:

在其他平台上也有类似的工具。 Python本身带有标准库中的timeit模块。 通常,对于上述时间,我们将直接使用timeit 。 但是, timeit难以可靠地对导入进行计时,因为Python聪明地只将模块导入一次。 考虑以下示例:

 $ python3.6 -m timeit $ python3.6 -m timeit "import typing"
"import typing"
10000000 loops, best of 3: 0.134 usec per loop
10000000 loops, best of 3: 0.134 usec per loop

While you get a result, you should be suspicious about the result: 0.1 microsecond is more than 100000 times faster than what perf measured! What timeit has actually done is to run the import typing statement 30 million times, with Python actually only importing typing once.

当你得到一个结果,你应该是可疑的结果:0.1微秒是比快超过10倍perf测! 什么timeit实际上做的是运行import typing声明3000万次,与Python实际上只是进口typing一次。

To get reasonable results you can tell timeit to only run once:

为了获得合理的结果,您可以告诉timeit只运行一次:

These results are on the same scale as the results from perf above. However, since these are based on only one execution of the code, they are not as reliable as those based on multiple runs.

这些结果与上述perf的结果具有相同的规模。 但是,由于这些仅基于代码的一次执行,因此它们不如基于多次运行的代码可靠。

The conclusion in both these cases is that importing typing takes a few milliseconds. For the majority of programs and scripts you write this will probably not be an issue.

在这两种情况下的结论是,导入typing需要花费几毫秒的时间。 对于您编写的大多数程序和脚本,这可能不会成为问题。

The New importtime Option

新的importtime选项

In Python 3.7 there is also a new command line option that can be used to figure out how much time imports take. Using -X importtime you’ll get a report about all imports that are made:

在Python 3.7中,还有一个新的命令行选项可用于确定导入需要花费多少时间。 使用-X importtime您将获得有关所有已完成导入的报告:

 $ python3.7 -X importtime import_typing.py
$ python3.7 -X importtime import_typing.py
import time: self [us] | cumulative | imported package
import time: self [us] | cumulative | imported package
[ ... some information hidden for brevity ... ]
[ ... some information hidden for brevity ... ]
import time:       358 |        358 | zipimport
import time:       358 |        358 | zipimport
import time:      2107 |      14610 | site
import time:      2107 |      14610 | site
import time:       272 |        272 |   collections.abc
import time:       272 |        272 |   collections.abc
import time:       664 |       3058 |   re
import time:       664 |       3058 |   re
import time:      3044 |       6373 | typing
import time:      3044 |       6373 | typing

This shows a similar result. Importing typing takes about 6 milliseconds. If you’ll read the report closely you can notice that around half of this time is spent on importing the collections.abc and re modules which typing depends on.

这显示了相似的结果。 导入typing大约需要6毫秒。 如果您仔细阅读该报告,您会注意到大约有一半的时间用于导入collections.abcre typing依赖的模块。

So, should you use static type checking in your own code? Well, it’s not an all-or-nothing question. Luckily, Python supports the concept of gradual typing. This means that you can gradually introduce types into your code. Code without type hints will be ignored by the static type checker. Therefore, you can start adding types to critical components, and continue as long as it adds value to you.

因此,您应该在自己的代码中使用静态类型检查吗? 好吧,这不是一个全有或全无的问题。 幸运的是,Python支持逐步类型化的概念。 这意味着您可以逐渐将类型引入代码中。 没有类型提示的代码将被静态类型检查器忽略。 因此,您可以开始向关键组件添加类型,并继续添加,只要它能为您增加价值即可。

Looking at the lists above of pros and cons you’ll notice that adding types will have no effect on your running program or the users of your program. Type checking is meant to make your life as a developer better and more convenient.

查看上面的利弊列表,您会发现添加类型对正在运行的程序或程序用户没有影响。 类型检查旨在使您作为开发人员的生活更美好,更便捷。

A few rules of thumb on whether to add types to your project are:

关于是否向项目中添加类型的一些经验法则是:

  • If you are just beginning to learn Python, you can safely wait with type hints until you have more experience.

  • Type hints add little value in short throw-away scripts.

  • In libraries that will be used by others, especially ones published on PyPI, type hints add a lot of value. Other code using your libraries need these type hints to be properly type checked itself. For examples of projects using type hints see cursive_re, black, our own Real Python Reader, and Mypy itself.

  • In bigger projects, type hints help you understand how types flow through your code, and are highly recommended. Even more so in projects where you cooperate with others.

  • 如果您刚刚开始学习Python,则可以放心使用类型提示,直到您有更多经验为止。

  • 简短的一次性脚本中,类型提示几乎没有任何价值。

  • 在其他人会使用的库中,尤其是在PyPI上发布的库中,类型提示会增加很多价值。 使用您的库的其他代码需要这些类型提示才能正确进行类型检查。 有关使用类型提示的项目的示例,请参见cursive_reblack ,我们自己的Real Python ReaderMypy本身。

  • 在较大的项目中,类型提示可帮助您了解类型如何在代码中流动,因此强烈建议使用。 在与他人合作的项目中更是如此。

In his excellent article The State of Type Hints in Python Bernát Gábor recommends that “type hints should be used whenever unit tests are worth writing.” Indeed, type hints play a similar role as tests in your code: they help you as a developer write better code.

BernátGábor在他的出色文章《 Python中的类型提示的状态》中建议“ 只要值得编写单元测试,就应使用类型提示 。” 实际上,类型提示在代码中的作用与测试相似:它们可以帮助您作为开发人员编写更好的代码。

Hopefully you now have an idea about how type checking works in Python and whether it’s something you would like to employ in your own projects.

希望您现在对Python中的类型检查如何工作以及是否要在自己的项目中使用它有所了解。

In the rest of this guide, we’ll go into more detail about the Python type system, including how you run static type checkers (with particular focus on Mypy), how you type check code that uses libraries without type hints, and how you use annotations at runtime.

在本指南的其余部分,我们将详细介绍Python类型系统,包括如何运行静态类型检查器(特别关注Mypy),如何键入使用不带类型提示的库的检查代码以及如何运行。在运行时使用注释。

注解 (Annotations)

Annotations were introduced in Python 3.0, originally without any specific purpose. They were simply a way to associate arbitrary expressions to function arguments and return values.

注释是在Python 3.0引入的 ,最初没有任何特定目的。 它们只是将任意表达式与函数参数和返回值关联的一种方式。

Years later, PEP 484 defined how to add type hints to your Python code, based off work that Jukka Lehtosalo had done on his Ph.D. project—Mypy. The main way to add type hints is using annotations. As type checking is becoming more and more common, this also means that annotations should mainly be reserved for type hints.

多年后, PEP 484根据Jukka Lehtosalo在其博士学位上所做的工作,定义了如何在您的Python代码中添加类型提示。 项目-Mypy。 添加类型提示的主要方法是使用注释。 随着类型检查变得越来越普遍,这也意味着注释应主要保留给类型提示。

The next sections explain how annotations work in the context of type hints.

下一节将说明注释在类型提示的上下文中如何工作。

功能注释 (Function Annotations)

For functions, you can annotate arguments and the return value. This is done as follows:

对于函数,您可以注释参数和返回值。 这样做如下:

For arguments the syntax is argument: annotation, while the return type is annotated using -> annotation. Note that the annotation must be a valid Python expression.

对于参数,语法为argument: annotation ,而返回类型使用-> annotation 。 请注意,注释必须是有效的Python表达式。

The following simple example adds annotations to a function that calculates the circumference of a circle:

下面的简单示例将注释添加到计算圆的周长的函数中:

 import import math

math

def def circumferencecircumference (( radiusradius : : floatfloat ) ) -> -> floatfloat :
    :
    return return 2 2 * * mathmath .. pi pi * * radius
radius

When running the code, you can also inspect the annotations. They are stored in a special .__annotations__ attribute on the function:

运行代码时,您也可以检查注释。 它们存储在函数的特殊.__annotations__属性中:

>>>
>>>
 >>>  circumference ( 1.23 )
7.728317927830891

>>>  circumference . __annotations__
{'radius': <class 'float'>, 'return': <class 'float'>}

Sometimes you might be confused by how Mypy is interpreting your type hints. For those cases there are special Mypy expressions: reveal_type() and reveal_locals(). You can add these to your code before running Mypy, and Mypy will dutifully report which types it has inferred. As an example, save the following code to reveal.py:

有时,您可能会对Mypy如何解释您的类型提示感到困惑。 对于这些情况,有一些特殊的Mypy表达式: reveal_type()reveal_locals() 。 您可以在运行Mypy之前将它们添加到代码中,然后Mypy会尽职报告其推断出的​​类型。 作为示例,将以下代码保存到reveal.py

Next, run this code through Mypy:

接下来,通过Mypy运行以下代码:

 $ mypy reveal.py
$ mypy reveal.py
reveal.py:4: error: Revealed type is 'builtins.float'

reveal.py:4: error: Revealed type is 'builtins.float'

reveal.py:8: error: Revealed local types are:
reveal.py:8: error: Revealed local types are:
reveal.py:8: error: circumference: builtins.float
reveal.py:8: error: circumference: builtins.float
reveal.py:8: error: radius: builtins.int
reveal.py:8: error: radius: builtins.int

Even without any annotations Mypy has correctly inferred the types of the built-in math.pi, as well as our local variables radius and circumference.

即使没有任何注释,Mypy也可以正确推断出内置math.pi的类型,以及我们的局部变量radiuscircumference

Note: The reveal expressions are only meant as a tool helping you add types and debug your type hints. If you try to run the reveal.py file as a Python script it will crash with a NameError since reveal_type() is not a function known to the Python interpreter.

注意:揭示表达式仅是帮助您添加类型和调试类型提示的工具。 如果您尝试以Python脚本的reveal.py运行reveal.py文件,它将由于NameError崩溃,因为reveal_type()不是Python解释器已知的函数。

If Mypy says that “Name ‘reveal_locals‘ is not defined” you might need to update your Mypy installation. The reveal_locals() expression is available in Mypy version 0.610 and later.

如果Mypy说“名称' reveal_locals '未定义”,则可能需要更新Mypy安装。 reveal_locals() 0.610及更高版本中提供了reveal_locals()表达式。

可变注释 (Variable Annotations)

In the definition of circumference() in the previous section, you only annotated the arguments and the return value. You did not add any annotations inside the function body. More often than not, this is enough.

在上一节的circumference()的定义中,您仅注释了参数和返回值。 您没有在函数体内添加任何注释。 通常,这足够了。

However, sometimes the type checker needs help in figuring out the types of variables as well. Variable annotations were defined in PEP 526 and introduced in Python 3.6. The syntax is the same as for function argument annotations:

但是,有时类型检查器在确定变量类型时也需要帮助。 变量注释在PEP 526中定义,并在Python 3.6中引入。 语法与函数参数注释的语法相同:

The variable pi has been annotated with the float type hint.

变量pi已使用float类型提示进行注释。

Note: Static type checkers are more than able to figure out that 3.142 is a float, so in this example the annotation of pi is not necessary. As you learn more about the Python type system, you’ll see more relevant examples of variable annotations.

注意:静态类型检查器不仅仅能够确定3.142是浮点数,因此在此示例中, pi的注释不是必需的。 当您了解有关Python类型系统的更多信息时,您将看到更多相关的变量注释示例。

Annotations of variables are stored in the module level __annotations__ dictionary:

变量的注释存储在模块级别的__annotations__字典中:

>>>
>>> circumference(1)
6.284

>>> __annotations__
{'pi': <class 'float'>}

>>>

You’re allowed to annotate a variable without giving it a value. This adds the annotation to the __annotations__ dictionary, while the variable remains undefined:

您可以在不给变量值的情况下对其进行注释。 这会将注释添加到__annotations__字典中,而变量仍未定义:

>>>
>>> nothing: str
>>> nothing
NameError: name 'nothing' is not defined

>>> __annotations__
{'nothing': <class 'str'>}

>>>

Since no value was assigned to nothing, the name nothing is not yet defined.

由于未将值赋给nothing ,因此尚未定义nothing

类型注释 (Type Comments)

As mentioned, annotations were introduced in Python 3, and they’ve not been backported to Python 2. This means that if you’re writing code that needs to support legacy Python, you can’t use annotations.

如前所述,注释是在Python 3中引入的,并且尚未向后移植到Python2。这意味着,如果要编写需要支持旧版Python的代码,则不能使用注释。

Instead, you can use type comments. These are specially formatted comments that can be used to add type hints compatible with older code. To add type comments to a function you do something like this:

相反,您可以使用类型注释。 这些是特殊格式的注释,可用于添加与旧代码兼容的类型提示。 要将类型注释添加到函数,请执行以下操作:

 import import math

math

def def circumferencecircumference (( radiusradius ):
):
# type: (float) -> float
# type: (float) -> float
return return 2 2 * * mathmath .. pi pi * * radius
radius

The type comments are just comments, so they can be used in any version of Python.

类型注释只是注释,因此可以在任何版本的Python中使用。

Type comments are handled directly by the type checker, so these types are not available in the __annotations__ dictionary:

类型注释由类型检查器直接处理,因此这些类型在__annotations__词典中不可用:

>>>
>>>
 >>>  circumference . __annotations__
{}

A type comment must start with the type: literal, and be on the same or the following line as the function definition. If you want to annotate a function with several arguments, you write each type separated by comma:

类型注释必须以type:文字开头,并且与函数定义在同一行或下一行。 如果要用多个参数注释一个函数,请编写每个用逗号分隔的类型:

You are also allowed to write each argument on a separate line with its own annotation:

您还可以将每个参数用自己的注释写在单独的行上:

 # headlines.py

# headlines.py

def def headlineheadline (
    (
    texttext ,           ,           # type: str
    # type: str
    widthwidth == 8080 ,       ,       # type: int
    # type: int
    fill_charfill_char == "-""-" ,  ,  # type: str
# type: str
):                  ):                  # type: (...) -> str
    # type: (...) -> str
    return return ff " {text.title()} "" {text.title()} " .. centercenter (( widthwidth , , fill_charfill_char )

)

printprint (( headlineheadline (( "type comments work""type comments work" , , widthwidth == 4040 ))
))

Run the example through Python and Mypy:

通过Python和Mypy运行示例:

If you have errors, for instance if you happened to call headline() with width="full" on line 10, Mypy will tell you:

如果您遇到错误,例如,如果您碰巧在第10行调用了headline() ,其width="full" ,Mypy会告诉您:

 $ mypy headline.py
$ mypy headline.py
headline.py:10: error: Argument "width" to "headline" has incompatible
headline.py:10: error: Argument "width" to "headline" has incompatible
                       type "str"; expected "int"
                       type "str"; expected "int"

You can also add type comments to variables. This is done similarly to how you add type comments to arguments:

您还可以将类型注释添加到变量。 类似于将类型注释添加到参数的方法:

In this example, pi will be type checked as a float variable.

在此示例中,将pi作为浮点变量进行类型检查。

那么,类型注释或类型注释呢? (So, Type Annotations or Type Comments?)

Should you use annotations or type comments when adding type hints to your own code? In short: Use annotations if you can, use type comments if you must.

将类型提示添加到自己的代码中时,应该使用注释还是键入注释? 简而言之: 如果可以,请使用注释,如果需要,请使用类型注释。

Annotations provide a cleaner syntax keeping type information closer to your code. They are also the officially recommended way of writing type hints, and will be further developed and properly maintained in the future.

注释提供了更简洁的语法,使类型信息更接近您的代码。 它们也是书写类型提示的官方推荐方式 ,并且将来会得到进一步开发和适当维护。

Type comments are more verbose and might conflict with other kinds of comments in your code like linter directives. However, they can be used in code bases that don’t support annotations.

类型注释更加冗长,并且可能与代码中的其他类型的注释(例如linter指令)冲突。 但是,它们可以在不支持注释的代码库中使用。

There is also hidden option number three: stub files. You will learn about these later, when we discuss adding types to third party libraries.

还有第三个隐藏的选项: 存根文件 。 稍后,当我们讨论将类型添加到第三方库时,您将学到这些。

Stub files will work in any version of Python, at the expense of having to maintain a second set of files. In general, you only want to use stub files if you can’t change the original source code.

存根文件可在任何版本的Python中工作,但必须维护第二组文件。 通常,仅在无法更改原始源代码的情况下才想使用存根文件。

玩Python类型,第1部分 (Playing With Python Types, Part 1)

Up until now you’ve only used basic types like str, float, and bool in your type hints. The Python type system is quite powerful, and supports many kinds of more complex types. This is necessary as it needs to be able to reasonably model Python’s dynamic duck typing nature.

到目前为止,您仅在类型提示中使用了strfloatbool类的基本类型。 Python类型系统功能强大,并支持许多更复杂的类型。 这是必要的,因为它需要能够合理地建模Python的动态鸭子类型化性质。

In this section you will learn more about this type system, while implementing a simple card game. You will see how to specify:

在本节中,您将学习有关这种类型的系统的更多信息,同时实现一个简单的纸牌游戏。 您将看到如何指定:

After a short detour into some type theory you will then see even more ways to specify types in Python. You can find the code examples from this section here.

简短地绕过一些类型理论之后,您将看到更多在Python中指定类型的方法 。 你可以找到本节中的代码示例在这里

示例:一副纸牌 (Example: A Deck of Cards)

The following example shows an implementation of a regular (French) deck of cards:

以下示例显示了常规(法语)纸牌的实现

 # game.py

# game.py

import import random

random

SUITS SUITS = = "♠ ♡ ♢ ♣""♠ ♡ ♢ ♣" .. splitsplit ()
()
RANKS RANKS = = "2 3 4 5 6 7 8 9 10 J Q K A""2 3 4 5 6 7 8 9 10 J Q K A" .. splitsplit ()

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值