Python 3.7的超酷新功能

Python 3.7 is officially released! This new Python version has been in development since September 2016, and now we all get to enjoy the results of the core developers’ hard work.

Python 3.7正式发布! 这个新的Python版本自20169月开始开发,现在我们所有人都可以享受核心开发人员辛勤工作的成果。

What does the new Python version bring? While the documentation gives a good overview of the new features, this article will take a deep dive into some of the biggest pieces of news. These include:

新的Python版本带来了什么? 尽管文档很好地概述了这些新功能,但本文将深入探讨一些重大新闻。 这些包括:

  • Easier access to debuggers through a new breakpoint() built-in
  • Simple class creation using data classes
  • Customized access to module attributes
  • Improved support for type hinting
  • Higher precision timing functions
  • 通过内置的新breakpoint()更轻松地访问调试器
  • 使用数据类创建简单的类
  • 定制访问模块属性
  • 改进了对类型提示的支持
  • 高精度计时功能

More importantly, Python 3.7 is fast.

更重要的是,Python 3.7速度很快。

In the final sections of this article, you’ll read more about this speed, as well as some of the other cool features of Python 3.7. You will also get some advice on upgrading to the new version.

在本文的最后几节中,您将了解有关此速度的更多信息,以及Python 3.7的一些其他出色功能。 您还将获得有关升级到新版本的一些建议。

内置breakpoint() (The breakpoint() Built-In)

While we might strive to write perfect code, the simple truth is that we never do. Debugging is an important part of programming. Python 3.7 introduces the new built-in function breakpoint(). This does not really add any new functionality to Python, but it makes using debuggers more flexible and intuitive.

虽然我们可能会努力编写完美的代码,但简单的事实是我们从不这样做。 调试是编程的重要组成部分。 Python 3.7引入了新的内置函数breakpoint() 。 这实际上并没有为Python添加任何新功能,但是它使调试器的使用更加灵活和直观。

Assume that you have the following buggy code in the file bugs.py:

假设您在bugs.py文件中有以下错误代码:

 def def dividedivide (( ee , , ff ):
    ):
    return return f f / / e

e

aa , , b b = = 00 , , 1
1
printprint (( dividedivide (( aa , , bb ))
))

Running the code causes a ZeroDivisionError inside the divide() function. Let’s say that you want to interrupt your code and drop into a debugger right at the top of divide(). You can do so by setting a so called “breakpoint” in your code:

运行代码会在ZeroDivisionError divide()函数内部导致ZeroDivisionError 。 假设您要中断代码并直接在divide()顶部放入调试器 。 您可以通过在代码中设置一个所谓的“断点”来实现:

A breakpoint is a signal inside your code that execution should temporarily stop, so that you can look around at the current state of the program. How do you place the breakpoint? In Python 3.6 and below, you use this somewhat cryptic line:

断点是代码内部的信号,该信号应暂时停止执行,以便您可以查看程序的当前状态。 您如何放置断点? 在Python 3.6及以下版本中,您可以使用以下含糊的代码行:

 def def dividedivide (( ee , , ff ):
    ):
    import import pdbpdb ; ; pdbpdb .. set_traceset_trace ()
    ()
    return return f f / / e
e

Here, pdb is the Python Debugger from the standard library. In Python 3.7, you can use the new breakpoint() function call as a shortcut instead:

在这里, pdb是标准库中的Python调试器。 在Python 3.7中,可以将新的breakpoint()函数调用用作快捷方式:

In the background, breakpoint() is first importing pdb and then calling pdb.set_trace() for you. The obvious benefits are that breakpoint() is easier to remember and that you only need to type 12 characters instead of 27. However, the real bonus of using breakpoint() is its customizability.

在后台, breakpoint()首先导入pdb ,然后为您调用pdb.set_trace() 。 明显的好处是breakpoint()更容易记住,只需要键入12个字符而不是27个字符即可。但是,使用breakpoint()的真正好处是它的可定制性。

Run your bugs.py script with breakpoint():

使用breakpoint()运行您的bugs.py脚本:

 $ python3.7 bugs.py 
$ python3.7 bugs.py 
> /home/gahjelle/bugs.py> /home/gahjelle/bugs.py (( 33 )divide) divide ()
()
-> return f / e
-> return f / e
(Pdb)
(Pdb)

The script will break when it reaches breakpoint() and drop you into a PDB debugging session. You can type c and hit Enter to continue the script. Refer to Nathan Jennings’ PDB guide if you want to learn more about PDB and debugging.

该脚本在到达breakpoint()时将中断,并使您进入PDB调试会话。 您可以键入c并按Enter键继续执行脚本。 如果您想了解有关PDB和调试的更多信息,请参考Nathan Jennings的PDB指南

Now, say that you think you’ve fixed the bug. You would like to run the script again but without stopping in the debugger. You could, of course, comment out the breakpoint() line, but another option is to use the PYTHONBREAKPOINT environment variable. This variable controls the behavior of breakpoint(), and setting PYTHONBREAKPOINT=0 means that any call to breakpoint() is ignored:

现在,假设您认为已修复该错误。 您想再次运行脚本,但不停止调试器。 当然,您可以注释掉breakpoint()行,但是另一种选择是使用PYTHONBREAKPOINT环境变量。 此变量控制breakpoint()的行为,设置PYTHONBREAKPOINT=0意味着对breakpoint()任何调用都将被忽略:

Oops, it seems as if you haven’t fixed the bug after all…

糟糕,看来您毕竟还没有修复错误…

Another option is to use PYTHONBREAKPOINT to specify a debugger other than PDB. For instance, to use PuDB (a visual debugger in the console) you can do:

另一种选择是使用PYTHONBREAKPOINT指定除PDB之外的其他调试器。 例如,要使用PuDB (控制台中的可视调试器),您可以执行以下操作:

 $ $ PYTHONBREAKPOINTPYTHONBREAKPOINT =pudb.set_trace python3.7 bugs.py
= pudb.set_trace python3.7 bugs.py

For this to work, you need to have pudb installed (pip install pudb). Python will take care of importing pudb for you though. This way you can also set your default debugger. Simply set the PYTHONBREAKPOINT environment variable to your preferred debugger. See this guide for instructions on how to set an environment variable on your system.

对于这个工作,你需要有pudb安装( pip install pudb )。 Python会pudb您导入pudb 。 这样,您还可以设置默认调试器。 只需将PYTHONBREAKPOINT环境变量设置为您首选的调试器。 请参阅本指南以获取有关如何在系统上设置环境变量的说明。

The new breakpoint() function does not only work with debuggers. One convenient option could be to simply start an interactive shell inside your code. For instance, to start an IPython session, you can use the following:

新的breakpoint()函数不仅适用于调试器。 一种方便的选择是仅在代码内部启动一个交互式外壳。 例如,要启动IPython会话,可以使用以下命令:

You can also create your own function and have breakpoint() call that. The following code prints all variables in the local scope. Add it to a file called bp_utils.py:

您也可以创建自己的函数,并使用breakpoint()调用。 以下代码在本地范围内打印所有变量。 将其添加到名为bp_utils.py的文件中:

 from from pprint pprint import import pprint
pprint
import import sys

sys

def def print_localsprint_locals ():
    ():
    caller caller = = syssys .. _getframe_getframe (( 11 )  )  # Caller is 1 frame up.
    # Caller is 1 frame up.
    pprintpprint (( callercaller .. f_localsf_locals )
)

To use this function, set PYTHONBREAKPOINT as before, with the <module>.<function> notation:

要使用此功能, PYTHONBREAKPOINT像以前一样使用<module>.<function>表示法设置PYTHONBREAKPOINT

Normally, breakpoint() will be used to call functions and methods that do not need arguments. However, it is possible to pass arguments as well. Change the line breakpoint() in bugs.py to:

通常, breakpoint()将用于调用不需要参数的函数和方法。 但是,也可以传递参数。 将bugs.py的line breakpoint() bugs.py为:

 breakpointbreakpoint (( ee , , ff , , endend == "<-END"<-END nn "" )
)

Note: The default PDB debugger will raise a TypeError at this line because pdb.set_trace() does not take any positional arguments.

注意:默认的PDB调试器将在此行TypeError ,因为pdb.set_trace()不接受任何位置参数。

Run this code with breakpoint() masquerading as the print() function to see a simple example of the arguments being passed through:

伪装有breakpoint()作为print()函数运行以下代码,以查看传递的参数的简单示例:

See PEP 553 as well as the documentation for breakpoint() and sys.breakpointhook() for more information.

有关更多信息,请参见PEP 553以及breakpoint()sys.breakpointhook()的文档。

资料类别 (Data Classes)

The new dataclasses module makes it more convenient to write your own classes, as special methods like .__init__(), .__repr__(), and .__eq__() are added automatically. Using the @dataclass decorator, you can write something like:

新的dataclasses模块使编写自己的类更加方便,因为会自动添加.__init__().__repr__().__eq__()类的特殊方法。 使用@dataclass装饰器,您可以编写如下内容:

 from from dataclasses dataclasses import import dataclassdataclass , , field

field

@dataclass@dataclass (( orderorder == TrueTrue )
)
class class CountryCountry :
    :
    namename : : str
    str
    populationpopulation : : int
    int
    areaarea : : float float = = fieldfield (( reprrepr == FalseFalse , , comparecompare == FalseFalse )
    )
    coastlinecoastline : : float float = = 0

    0

    def def beach_per_personbeach_per_person (( selfself ):
        ):
        """Meters of coastline per person"""
        """Meters of coastline per person"""
        return return (( selfself .. coastline coastline * * 10001000 ) ) / / selfself .. population
population

These nine lines of code stand in for quite a bit of boilerplate code and best practices. Think about what it would take to implement Country as a regular class: the .__init__() method, a repr, six different comparison methods as well as the .beach_per_person() method. You can expand the box below to see an implementation of Country that is roughly equivalent to the data class:

这九行代码代表了很多样板代码和最佳实践。 考虑将Country用作常规类将需要执行什么: .__init__()方法,一个repr ,六个不同的比较方法以及.beach_per_person()方法。 您可以展开下面的框,以查看与数据类大致等效的Country的实现:

After creation, a data class is a normal class. You can, for instance, inherit from a data class in the normal way. The main purpose of data classes is to make it quick and easy to write robust classes, in particular small classes that mainly store data.

创建后,数据类是普通类。 例如,您可以按常规方式从数据类继承。 数据类的主要目的是使编写健壮的类(尤其是主要存储数据的小类)变得快速便捷。

You can use the Country data class like any other class:

您可以像其他任何类别一样使用Country数据类别:

 >>> >>>  norway norway = = CountryCountry (( "Norway""Norway" , , 53200455320045 , , 323802323802 , , 5813358133 )
)
>>> >>>  norway
norway
Country(name='Norway', population=5320045, coastline=58133)

Country(name='Norway', population=5320045, coastline=58133)

>>> >>>  norwaynorway .. area
area
323802

323802

>>> >>>  usa usa = = CountryCountry (( "United States""United States" , , 326625791326625791 , , 98335179833517 , , 1992419924 )
)
>>> >>>  nepal nepal = = CountryCountry (( "Nepal""Nepal" , , 2938429729384297 , , 147181147181 )
)
>>> >>>  nepal
nepal
Country(name='Nepal', population=29384297, coastline=0)

Country(name='Nepal', population=29384297, coastline=0)

>>> >>>  usausa .. beach_per_personbeach_per_person ()
()
0.06099946957342386

0.06099946957342386

>>> >>>  norwaynorway .. beach_per_personbeach_per_person ()
()
10.927163210085629
10.927163210085629

Note that all the fields .name, .population, .area, and .coastline are used when initializing the class (although .coastline is optional, as is shown in the example of landlocked Nepal). The Country class has a reasonable repr, while defining methods works the same as for regular classes.

请注意,初始化类时,将使用所有字段.name.population.area.coastline (尽管.coastline是可选的,如内陆尼泊尔的示例所示)。 Country类具有合理的repr ,而定义方法的作用与常规类相同。

By default, data classes can be compared for equality. Since we specified order=True in the @dataclass decorator, the Country class can also be sorted:

默认情况下,可以比较数据类的相等性。 由于我们在@dataclass装饰器中指定了order=True ,因此Country类也可以排序:

The sorting happens on the field values, first .name then .population, and so on. However, if you use field(), you can customize which fields will be used in the comparison. In the example, the .area field was left out of the repr and the comparisons.

排序发生在字段值上,首先是.name然后是.population ,依此类推。 但是,如果使用field() ,则可以自定义将在比较中使用的字段。 在示例中, .area字段未包含在repr和比较中。

Note: The country data are from the CIA World Factbook with population numbers estimated for July 2017.

注意:国家/地区数据来自CIA世界概况 ,估计2017年7月的人口数量。

Before you all go book your next beach holidays in Norway, here is what the Factbook says about the Norwegian climate: “temperate along coast, modified by North Atlantic Current; colder interior with increased precipitation and colder summers; rainy year-round on west coast.”

在大家预定下一个在挪威的海滩假期之前,这是《概况》对挪威气候的评价 :“沿海温带,经北大西洋洋流改造; 内部较冷,降水增加,夏天较冷; 西海岸终年降雨。”

Data classes do some of the same things as namedtuple. Yet, they draw their biggest inspiration from the attrs project. See our full guide to data classes for more examples and further information, as well as PEP 557 for the official description.

数据类与namedtuple做一些相同的事情。 但是,他们从attrs项目中获得了最大的启发。 有关更多示例和更多信息,请参见我们的数据类完整指南,对于正式说明,请参见PEP 557

定制模块属性 (Customization of Module Attributes)

Attributes are everywhere in Python! While class attributes are probably the most famous, attributes can actually be put on essentially anything—including functions and modules. Several of Python’s basic features are implemented as attributes: most of the introspection functionality, doc-strings, and name spaces. Functions inside a module are made available as module attributes.

Python中到处都有属性! 虽然类属性可能是最著名的,但实际上属性实际上可以放在任何东西上,包括函数和模块。 Python的一些基本功能被实现为属性:大多数自省功能,文档字符串和名称空间。 模块内部的功能可用作模块属性。

Attributes are most often retrieved using the dot notation: thing.attribute. However, you can also get attributes that are named at runtime using getattr():

最常使用点表示法来检索属性: thing.attribute 。 但是,您还可以使用getattr()获得在运行时命名的属性:

 import import random

random

random_attr random_attr = = randomrandom .. choicechoice (((( "gammavariate""gammavariate" , , "lognormvariate""lognormvariate" , , "normalvariate""normalvariate" ))
))
random_func random_func = = getattrgetattr (( randomrandom , , random_attrrandom_attr )

)

printprint (( ff "A "A  {random_attr}{random_attr}  random value: {random_func(1, 1)}" random value: {random_func(1, 1)}" )
)

Running this code will produce something like:

运行此代码将产生类似以下内容:

For classes, calling thing.attr will first look for attr defined on thing. If it is not found, then the special method thing.__getattr__("attr") is called. (This is a simplification. See this article for more details.) The .__getattr__() method can be used to customize access to attributes on objects.

对于类,调用thing.attr将首先查找在thing定义的attr 。 如果找不到,则调用特殊方法thing.__getattr__("attr") 。 (这是一种简化。有关更多详细信息,请参见本文 。) .__getattr__()方法可用于自定义对对象属性的访问。

Until Python 3.7, the same customization was not easily available for module attributes. However, PEP 562 introduces __getattr__() on modules, together with a corresponding __dir__() function. The __dir__() special function allows customization of the result of calling dir() on a module.

在Python 3.7之前,模块属性很难获得相同的自定义。 但是, PEP 562在模块上引入了__getattr__()以及相应的__dir__()函数。 __dir__()特殊功能允许自定义在模块上调用dir()的结果。

The PEP itself gives a few examples of how these functions can be used, including adding deprecation warnings to functions and lazy loading of heavy submodules. Below, we will build a simple plugin system that allows functions to be added to a module dynamically. This example takes advantage of Python packages. See this article if you need a refresher on packages.

PEP本身提供了一些如何使用这些功能的示例,包括向功能添加弃用警告以及延迟加载繁重的子模块。 下面,我们将构建一个简单的插件系统,该系统允许将功能动态添加到模块中。 这个例子利用了Python包。 如果您需要更新软件包,请参阅本文

Create a new directory, plugins, and add the following code to a file, plugins/__init__.py:

创建一个新目录plugins ,并将以下代码添加到一个文件plugins/__init__.py

 from from importlib importlib import import import_module
import_module
from from importlib importlib import import resources

resources

PLUGINS PLUGINS = = dictdict ()

()

def def register_pluginregister_plugin (( funcfunc ):
    ):
    """Decorator to register plug-ins"""
    """Decorator to register plug-ins"""
    name name = = funcfunc .. __name__
    __name__
    PLUGINSPLUGINS [[ namename ] ] = = func
    func
    return return func

func

def def __getattr____getattr__ (( namename ):
    ):
    """Return a named plugin"""
    """Return a named plugin"""
    trytry :
        :
        return return PLUGINSPLUGINS [[ namename ]
    ]
    except except KeyErrorKeyError :
        :
        _import_plugins_import_plugins ()
        ()
        if if name name in in PLUGINSPLUGINS :
            :
            return return PLUGINSPLUGINS [[ namename ]
        ]
        elseelse :
            :
            raise raise AttributeErrorAttributeError (
                (
                ff "module "module  {__name__!r}{__name__!r}  has no attribute  has no attribute  {name!r}{name!r} "
            "
            ) ) from from None

None

def def __dir____dir__ ():
    ():
    """List available plug-ins"""
    """List available plug-ins"""
    _import_plugins_import_plugins ()
    ()
    return return listlist (( PLUGINSPLUGINS .. keyskeys ())

())

def def _import_plugins_import_plugins ():
    ():
    """Import all resources to register plug-ins"""
    """Import all resources to register plug-ins"""
    for for name name in in resourcesresources .. contentscontents (( __name____name__ ):
        ):
        if if namename .. endswithendswith (( ".py"".py" ):
            ):
            import_moduleimport_module (( ff "" {__name__}{__name__} .. {name[:-3]}{name[:-3]} "" )
)

Before we look at what this code does, add two more files inside the plugins directory. First, let’s see plugins/plugin_1.py:

在我们看一下这段代码的作用之前,请在plugins目录中再添加两个文件。 首先,让我们看看plugins/plugin_1.py

Next, add similar code in the file plugins/plugin_2.py:

接下来,在文件plugins/plugin_2.py添加类似的代码:

 from from . . import import register_plugin

register_plugin

@register_plugin
@register_plugin
def def hello_2hello_2 ():
    ():
    printprint (( "Hello from Plugin 2""Hello from Plugin 2" )

)

@register_plugin
@register_plugin
def def goodbyegoodbye ():
    ():
    printprint (( "Plugin 2 says goodbye""Plugin 2 says goodbye" )
)

These plugins can now be used as follows:

现在可以按以下方式使用这些插件:

This may not all seem that revolutionary (and it probably isn’t), but let’s look at what actually happened here. Normally, to be able to call plugins.hello_1(), the hello_1() function must be defined in a plugins module or explicitly imported inside __init__.py in a plugins package. Here, it is neither!

这似乎并不全都是革命性的(也许不是),但让我们看看这里实际发生了什么。 通常,要能够调用plugins.hello_1() ,必须在plugins模块中定义hello_1()函数,或在plugins包中的__init__.py中显式导入hello_1()函数。 在这里,两者都不是!

Instead, hello_1() is defined in an arbitrary file inside the plugins package, and hello_1() becomes a part of the plugins package by registering itself using the @register_plugin decorator.

相反, hello_1()是在plugins包内的任意文件中定义的,并且hello_1()是通过使用@register_plugin 装饰器进行自身注册而成为plugins包的一部分。

The difference is subtle. Instead of the package dictating which functions are available, the individual functions register themselves as part of the package. This gives you a simple structure where you can add functions independently of the rest of the code without having to keep a centralized list of which functions are available.

区别是微妙的。 代替了指示哪些功能可用的程序包,各个功能将自身注册为程序包的一部分。 这为您提供了一个简单的结构,您可以独立于其余代码添加功能,而不必保留可用功能的集中列表。

Let us do a quick review of what __getattr__() does inside the plugins/__init__.py code. When you asked for plugins.hello_1(), Python first looks for a hello_1() function inside the plugins/__init__.py file. As no such function exists, Python calls __getattr__("hello_1") instead. Remember the source code of the __getattr__() function:

让我们快速回顾一下plugins/__init__.py代码中__getattr__()功能。 当您要求plugins.hello_1() ,Python首先在plugins/__init__.py文件中寻找hello_1()函数。 由于不存在这样的函数,Python会改为调用__getattr__("hello_1") 。 记住__getattr__()函数的源代码:

 def def __getattr____getattr__ (( namename ):
    ):
    """Return a named plugin"""
    """Return a named plugin"""
    trytry :
        :
        return return PLUGINSPLUGINS [[ namename ]        ]        # 1) Try to return plugin
    # 1) Try to return plugin
    except except KeyErrorKeyError :
        :
        _import_plugins_import_plugins ()           ()           # 2) Import all plugins
        # 2) Import all plugins
        if if name name in in PLUGINSPLUGINS :
            :
            return return PLUGINSPLUGINS [[ namename ]    ]    # 3) Try to return plugin again
        # 3) Try to return plugin again
        elseelse :
            :
            raise raise AttributeErrorAttributeError (   (   # 4) Raise error
                # 4) Raise error
                ff "module "module  {__name__!r}{__name__!r}  has no attribute  has no attribute  {name!r}{name!r} "
            "
            ) ) from from None
None

__getattr__() contains the following steps. The numbers in the following list correspond to the numbered comments in the code:

__getattr__()包含以下步骤。 下表中的数字与代码中带注释的数字相对应:

  1. First, the function optimistically tries to return the named plugin from the PLUGINS dictionary. This will succeed if a plugin named name exists and has already been imported.
  2. If the named plugin is not found in the PLUGINS dictionary, we make sure all plugins are imported.
  3. Return the named plugin if it has become available after the import.
  4. If the plugin is not in the PLUGINS dictionary after importing all plugins, we raise an AttributeError saying that name is not an attribute (plugin) on the current module.
  1. 首先,该函数乐观地尝试从PLUGINS字典返回命名的插件。 如果名为name的插件存在并且已经导入,则将成功。
  2. 如果在PLUGINS词典中找不到指定的插件,则确保所有插件都已导入。
  3. 如果导入后该命名插件变得可用,则返回该命名插件。
  4. 如果在导入所有插件之后该插件不在PLUGINS词典中,则我们引发AttributeErrorname不是当前模块上的属性(插件)。

How is the PLUGINS dictionary populated though? The _import_plugins() function imports all Python files inside the plugins package, but does not seem to touch PLUGINS:

PLUGINS词典如何填充? _import_plugins()函数导入了plugins包内的所有Python文件,但似乎没有碰到PLUGINS

Don’t forget that each plugin function is decorated by the @register_plugin decorator. This decorator is called when the plugins are imported and is the one actually populating the PLUGINS dictionary. You can see this if you manually import one of the plugin files:

不要忘记,每个插件功能都由@register_plugin装饰器装饰。 导入插件时将调用此装饰器,它实际上是填充PLUGINS字典的装饰器。 如果您手动导入插件文件之一,则可以看到以下内容:

 >>> >>>  import import plugins
plugins
>>> >>>  pluginsplugins .. PLUGINS
PLUGINS
{}

{}

>>> >>>  import import plugins.plugin_1
plugins.plugin_1
>>> >>>  pluginsplugins .. PLUGINS
PLUGINS
{'hello_1': <function hello_1 at 0x7f29d4341598>}
{'hello_1': <function hello_1 at 0x7f29d4341598>}

Continuing the example, note that calling dir() on the module also imports the remaining plugins:

继续该示例,请注意,在模块上调用dir()也会导入其余的插件:

dir() usually lists all available attributes on an object. Normally, using dir() on a module results in something like this:

dir()通常列出对象上所有可用的属性。 通常,在模块上使用dir()导致类似以下情况:

 >>> >>>  import import plugins
plugins
>>> >>>  dirdir (( pluginsplugins )
)
['PLUGINS', '__builtins__', '__cached__', '__doc__',
['PLUGINS', '__builtins__', '__cached__', '__doc__',
 '__file__', '__getattr__', '__loader__', '__name__',
 '__file__', '__getattr__', '__loader__', '__name__',
 '__package__', '__path__', '__spec__', '_import_plugins',
 '__package__', '__path__', '__spec__', '_import_plugins',
 'import_module', 'register_plugin', 'resources']
 'import_module', 'register_plugin', 'resources']

While this might be useful information, we are more interested in exposing the available plugins. In Python 3.7, you can customize the result of calling dir() on a module by adding a __dir__() special function. For plugins/__init__.py, this function first makes sure all plugins have been imported and then lists their names:

尽管这可能是有用的信息,但我们对公开可用的插件更感兴趣。 在Python 3.7中,您可以通过添加__dir__()特殊函数来自定义在模块上调用dir()的结果。 对于plugins/__init__.py ,此函数首先确保已导入所有插件,然后列出其名称:

Before leaving this example, please note that we also used another cool new feature of Python 3.7. To import all modules inside the plugins directory, we used the new importlib.resources module. This module gives access to files and resources inside modules and packages without the need for __file__ hacks (which do not always work) or pkg_resources (which is slow). Other features of importlib.resources will be highlighted later.

在离开此示例之前,请注意,我们还使用了Python 3.7的另一个很酷的新功能。 要导入plugins目录内的所有模块,我们使用了新的importlib.resources模块。 通过此模块,可以访问模块和软件包中的文件和资源,而无需__file__ hacks(不一定总是有效)或pkg_resources (很慢)。 importlib.resources其他功能将在稍后突出显示

键入增强 (Typing Enhancements)

Type hinting and annotations have been in constant development throughout the Python 3 series of releases. Python’s typing system is now quite stable. Still, Python 3.7 brings some enhancements to the table: better performance, core support, and forward references.

在整个Python 3系列发行版中,类型提示和注释一直在不断发展。 Python的打字系统现在非常稳定。 尽管如此,Python 3.7对该表进行了一些增强:更好的性能,核心支持和正向引用。

Python does not do any type checking at runtime (unless you are explicitly using packages like enforce). Therefore, adding type hints to your code should not affect its performance.

Python在运行时不进行任何类型检查(除非您显式使用诸如enforce包)。 因此,在代码中添加类型提示不会影响其性能。

Unfortunately, this is not completely true as most type hints need the typing module. The typing module is one of the slowest modules in the standard library. PEP 560 adds some core support for typing in Python 3.7, which significantly speeds up the typing module. The details of this are in general not necessary to know about. Simply lean back and enjoy the increased performance.

不幸的是,这并非完全正确,因为大多数类型提示都需要typing模块。 typing模块是标准库中最慢的模块之一。 PEP 560为Python 3.7中的键入添加了一些核心支持,从而大大加快了typing模块的速度。 通常不需要了解其详细信息。 只需向后倾斜即可享受更高的性能。

While Python’s type system is reasonably expressive, one issue that causes some pain is forward references. Type hints—or more generally annotations—are evaluated while the module is imported. Therefore, all names must already be defined before they are used. The following is not possible:

尽管Python的类型系统具有合理的表现力,但引起麻烦的一个问题是前向引用。 导入模块时会评估类型提示(或更一般地说是注释)。 因此,在使用所有名称之前,必须已经定义了所有名称。 以下是不可能的:

 class class TreeTree :
    :
    def def __init____init__ (( selfself , , leftleft : : TreeTree , , rightright : : TreeTree ) ) -> -> NoneNone :
        :
        selfself .. left left = = left
        left
        selfself .. right right = = right
right

Running the code raises a NameError because the class Tree is not yet (completely) defined in the definition of the .__init__() method:

运行代码会引发NameError因为尚未在.__init__()方法的定义中完全定义类Tree

To overcome this, you would have needed to write "Tree" as a string literal instead:

为了克服这个问题,您将需要编写"Tree"作为字符串文字:

 class class TreeTree :
    :
    def def __init____init__ (( selfself , , leftleft : : "Tree""Tree" , , rightright : : "Tree""Tree" ) ) -> -> NoneNone :
        :
        selfself .. left left = = left
        left
        selfself .. right right = = right
right

See PEP 484 for the original discussion.

有关原始讨论,请参见PEP 484

In a future Python 4.0, such so called forward references will be allowed. This will be handled by not evaluating annotations until that is explicitly asked for. PEP 563 describes the details of this proposal. In Python 3.7, forward references are already available as a __future__ import. You can now write the following:

在未来的Python 4.0中 ,将允许使用所谓的前向引用。 除非明确要求,否则不评估注释来解决此问题。 PEP 563描述了该提议的细节。 在Python 3.7中,前向引用已作为__future__ import提供 。 您现在可以编写以下内容:

Note that in addition to avoiding the somewhat clumsy "Tree" syntax, the postponed evaluation of annotations will also speed up your code, since type hints are not executed. Forward references are already supported by mypy.

请注意,除了避免使用笨拙的"Tree"语法外,对注释的延迟评估也将加快代码的速度,因为不会执行类型提示。 mypy已经支持前向引用。

By far, the most common use of annotations is type hinting. Still, you have full access to the annotations at runtime and can use them as you see fit. If you are handling annotations directly, you need to deal with the possible forward references explicitly.

到目前为止,注释最常见的用法是类型提示。 尽管如此,您仍可以在运行时完全访问注释,并可以根据需要使用它们。 如果直接处理批注,则需要显式处理可能的正向引用。

Let us create some admittedly silly examples that show when annotations are evaluated. First we do it old-style, so annotations are evaluated at import time. Let anno.py contain the following code:

让我们创建一些公认的愚蠢示例,这些示例显示何时评估注释。 首先,我们采用旧样式,因此在导入时会评估注释。 让anno.py包含以下代码:

 def def greetgreet (( namename : : printprint (( "Now!""Now!" )):
    )):
    printprint (( ff "Hello "Hello  {name}{name} "" )
)

Note that the annotation of name is print(). This is only to see exactly when the annotation is evaluated. Import the new module:

注意, name的注释是print() 。 这只是为了准确查看注释的时间。 导入新模块:

As you can see, the annotation was evaluated at import time. Note that name ends up annotated with None because that is the return value of print().

如您所见,注释是在导入时评估的。 注意, name最终以None注释,因为那是print()的返回值。

Add the __future__ import to enable postponed evaluation of annotations:

添加__future__导入以启用注释的延迟评估:

 from from __future__ __future__ import import annotations

annotations

def def greetgreet (( namename : : printprint (( "Now!""Now!" )):
    )):
    printprint (( ff "Hello "Hello  {name}{name} "" )
)

Importing this updated code will not evaluate the annotation:

导入此更新的代码将不会评估注释:

Note that Now! is never printed and the annotation is kept as a string literal in the __annotations__ dictionary. In order to evaluate the annotation, use typing.get_type_hints() or eval():

注意, Now! 永远不会打印出来,并且注释会在__annotations__字典中保留为字符串文字。 为了评估注释,请使用typing.get_type_hints()eval()

 >>> >>>  import import typing
typing
>>> >>>  typingtyping .. get_type_hintsget_type_hints (( annoanno .. greetgreet )
)
Now!
Now!
{'name': <class 'NoneType'>}

{'name': <class 'NoneType'>}

>>> >>>  evaleval (( annoanno .. greetgreet .. __annotations____annotations__ [[ "name""name" ])
])
Now!

Now!

>>> >>>  annoanno .. greetgreet .. __annotations__
__annotations__
{'name': "print('Now!')"}
{'name': "print('Now!')"}

Observe that the __annotations__ dictionary is never updated, so you need to evaluate the annotation every time you use it.

请注意, __annotations__字典从未更新,因此您每次使用注释时都需要对其进行评估。

计时精度 (Timing Precision)

In Python 3.7, the time module gains some new functions as described in PEP 564. In particular, the following six functions are added:

在Python 3.7中, time模块获得了一些新功能,如PEP 564中所述 。 特别是,添加了以下六个功能:

  • clock_gettime_ns(): Returns the time of a specified clock
  • clock_settime_ns(): Sets the time of a specified clock
  • monotonic_ns(): Returns the time of a relative clock that cannot go backwards (for instance due to daylight savings)
  • perf_counter_ns(): Returns the value of a performance counter—a clock specifically designed to measure short intervals
  • process_time_ns(): Returns the sum of the system and user CPU time of the current process (not including sleep time)
  • time_ns(): Returns the number of nanoseconds since January 1st 1970
  • clock_gettime_ns()返回指定时钟的时间
  • clock_settime_ns()设置指定时钟的时间
  • monotonic_ns()返回不能倒退的相对时钟的时间(例如,由于夏令时)
  • perf_counter_ns() :返回性能计数器的值,该时钟专门用于测量短间隔
  • process_time_ns()返回当前进程的系统和用户CPU时间的总和(不包括睡眠时间)
  • time_ns() :返回自1970年1月1日以来的纳秒数

In a sense, there is no new functionality added. Each function is similar to an already existing function without the _ns suffix. The difference being that the new functions return a number of nanoseconds as an int instead of a number of seconds as a float.

从某种意义上说,没有添加任何新功能。 每个函数都类似于不带_ns后缀的现有函数。 不同之处在于,新函数以int返回的时间为纳秒,而不是float的秒数。

For most applications, the difference between these new nanosecond functions and their old counterpart will not be appreciable. However, the new functions are easier to reason about because they rely on int instead of float. Floating point numbers are by nature inaccurate:

对于大多数应用程序,这些新的纳秒级功能与旧的纳秒级功能之间的差异将不明显。 但是,新函数更容易推论,因为它们依赖于int而不是float 。 浮点数本质上是不准确的

This is not an issue with Python but rather a consequence of computers needing to represent infinite decimal numbers using a finite number of bits.

这不是Python的问题,而是计算机需要使用有限数量的位表示无限十进制数的结果。

A Python float follows the IEEE 754 standard and uses 53 significant bits. The result is that any time greater than about 104 days (2⁵³ or approximately 9 quadrillion nanoseconds) cannot be expressed as a float with nanosecond precision. In contrast, a Python int is unlimited, so an integer number of nanoseconds will always have nanosecond precision independent of the time value.

Python float遵循IEEE 754标准,并使用53个有效位。 结果是,任何大于约104天(2·3³或约9万亿纳秒 )的时间都不能表示为具有纳秒精度的浮点数。 相反,Python int是无限制的 ,因此整数纳秒将始终具有纳秒精度,而与时间值无关。

As an example, time.time() returns the number of seconds since January 1st 1970. This number is already quite big, so the precision of this number is at the microsecond level. This function is the one showing the biggest improvement in its _ns version. The resolution of time.time_ns() is about 3 times better than for time.time().

例如, time.time()返回自1970年1月1日以来的秒数。此数字已经很大,因此此数字的精度为微秒级别。 此函数是_ns版本中显示最大改进的_ns 。 的分辨率time.time_ns()3倍的time.time()

What is a nanosecond by the way? Technically, it is one billionth of a second, or 1e-9 second if you prefer scientific notation. These are just numbers though and do not really provide any intuition. For a better visual aid, see Grace Hopper’s wonderful demonstration of the nanosecond.

顺便说一下,纳秒是多少? 从技术上讲,如果您更喜欢科学计数法,则为十亿分之一秒,即1e-9秒。 这些只是数字,并不能提供任何直觉。 有关更好的视觉帮助,请参见Grace Hopper的 纳秒级精彩演示

As an aside, if you need to work with datetimes with nanosecond precision, the datetime standard library will not cut it. It explicitly only handles microseconds:

顺便说一句,如果您需要使用纳秒精度的datetime ,则datetime标准库将不会削减它。 它显式只处理微秒:

 >>> >>>  from from datetime datetime import import datetimedatetime , , timedelta
timedelta
>>> >>>  datetimedatetime (( 20182018 , , 66 , , 2727 ) ) + + timedeltatimedelta (( secondsseconds == 1e-61e-6 )
)
datetime.datetime(2018, 6, 27, 0, 0, 0, 1)

datetime.datetime(2018, 6, 27, 0, 0, 0, 1)

>>> >>>  datetimedatetime (( 20182018 , , 66 , , 2727 ) ) + + timedeltatimedelta (( secondsseconds == 1e-91e-9 )
)
datetime.datetime(2018, 6, 27, 0, 0)
datetime.datetime(2018, 6, 27, 0, 0)

Instead, you can use the astropy project. Its astropy.time package represents datetimes using two float objects which guarantees “sub-nanosecond precision over times spanning the age of the universe.”

相反,您可以使用astropy项目 。 它的astropy.time包使用两个float对象表示日期时间,从而保证“在整个宇宙时代的时间范围内,亚纳秒精度”。

The latest version of astropy is available in Python 3.5 and later.

astropy的最新版本在Python 3.5及更高版本中可用。

其他很酷的功能 (Other Pretty Cool Features)

So far, you have seen the headline news regarding what’s new in Python 3.7. However, there are many other changes that are also pretty cool. In this section, we will look briefly at some of them.

到目前为止,您已经看到有关Python 3.7新增功能的头条新闻。 但是,还有许多其他变化也很酷。 在本节中,我们将简要介绍其中的一些。

保证字典顺序 (The Order of Dictionaries Is Guaranteed)

The CPython implementation of Python 3.6 has ordered dictionaries. (PyPy also has this.) This means that items in dictionaries are iterated over in the same order they were inserted. The first example is using Python 3.5, and the second is using Python 3.6:

Python 3.6的CPython实现对字典进行了排序。 ( PyPy也有此功能。)这意味着字典中的项目将按照插入时的相同顺序进行迭代。 第一个示例使用Python 3.5,第二个示例使用Python 3.6:

 >>> >>>  {{ "one""one" : : 11 , , "two""two" : : 22 , , "three""three" : : 33 }  }  # Python <= 3.5
# Python <= 3.5
{'three': 3, 'one': 1, 'two': 2}

{'three': 3, 'one': 1, 'two': 2}

>>> >>>  {{ "one""one" : : 11 , , "two""two" : : 22 , , "three""three" : : 33 }  }  # Python >= 3.6
# Python >= 3.6
{'one': 1, 'two': 2, 'three': 3}
{'one': 1, 'two': 2, 'three': 3}

In Python 3.6, this ordering was just a nice consequence of that implementation of dict. In Python 3.7, however, dictionaries preserving their insert order is part of the language specification. As such, it may now be relied on in projects that support only Python >= 3.7 (or CPython >= 3.6).

在Python 3.6中,这种排序只是dict实现的一个不错的结果。 但是,在Python 3.7中,保留其插入顺序的字典是语言规范的一部分。 因此,现在只能在仅支持Python> = 3.7(或CPython> = 3.6)的项目中使用它。

async ”和“ await ”是关键字 (async” and “await” Are Keywords)

Python 3.5 introduced coroutines with async and await syntax. To avoid issues of backwards compatibility, async and await were not added to the list of reserved keywords. In other words, it was still possible to define variables or functions named async and await.

Python 3.5引入了带有asyncawait语法的协程 。 为了避免向后兼容的问题,没有将asyncawait添加到保留关键字列表中。 换句话说,仍然可以定义名为asyncawait变量或函数。

In Python 3.7, this is no longer possible:

在Python 3.7中,这不再可行:

asyncio ”瘦脸 (asyncio” Face Lift)

The asyncio standard library was originally introduced in Python 3.4 to handle concurrency in a modern way using event loops, coroutines and futures. Here is a gentle introduction.

asyncio标准库最初是在Python 3.4中引入的,它使用事件循环,协程和期货以现代方式处理并发。 这是一个简短的介绍

In Python 3.7, the asyncio module is getting a major face lift, including many new functions, support for the context variables mentioned above, and performance improvements. Of particular note is asyncio.run(), which simplifies calling coroutines from synchronous code. Using asyncio.run(), you do not need to explicitly create the event loop. An asynchronous Hello World program can now be written:

在Python 3.7中, asyncio模块得到了重大改进 ,包括许多新功能,对上述上下文变量的支持以及性能改进。 特别要注意的是asyncio.run() ,它简化了从同步代码中调用协程的过程。 使用asyncio.run() ,您无需显式创建事件循环。 现在可以编写异步Hello World程序:

 import import asyncio

asyncio

async async def def hello_worldhello_world ():
    ():
    printprint (( "Hello World!""Hello World!" )

)

asyncioasyncio .. runrun (( hello_worldhello_world ())
())

上下文变量 (Context Variables)

Context variables are variables that can have different values depending on their context. They are similar to Thread-Local Storage in which each execution thread may have a different value for a variable. However, with context variables, there may be several contexts in one execution thread. The main use case for context variables is keeping track of variables in concurrent asynchronous tasks.

上下文变量是根据上下文可以具有不同值的变量。 它们类似于“线程本地存储”,其中每个执行线程的变量值可能不同。 但是,使用上下文变量,一个执行线程中可能有多个上下文。 上下文变量的主要用例是跟踪并发异步任务中的变量。

The following example constructs three contexts, each with their own value for the value name. The greet() function is later able to use the value of name inside each context:

以下示例构造了三个上下文,每个上下文都有其自己的值name值。 greet()函数以后可以在每个上下文中使用name的值:

Running this script greets Steve, Dina, and Harry in reverse order:

运行此脚本以相反的顺序向Steve,Dina和Harry致意:

 $ python3.7 context_demo.py
$ python3.7 context_demo.py
Hello Harry
Hello Harry
Hello Dina
Hello Dina
Hello Steve
Hello Steve

使用“ importlib.resources ”导入数据文件 (Importing Data Files With “importlib.resources)

One challenge when packaging a Python project is deciding what to do with project resources like data files needed by the project. A few options have commonly been used:

打包Python项目时的一个挑战是决定如何处理项目资源,例如项目所需的数据文件。 通常使用一些选项:

  • Hard-code a path to the data file.
  • Put the data file inside the package and locate it using __file__.
  • Use setuptools.pkg_resources to access the data file resource.
  • 硬编码数据文件的路径。
  • 将数据文件放入包中,并使用__file__定位。
  • 使用setuptools.pkg_resources访问数据文件资源。

Each of these have their shortcomings. The first option is not portable. Using __file__ is more portable, but if the Python project is installed it might end up inside a zip and not have a __file__ attribute. The third option solves this problem, but is unfortunately very slow.

这些都有各自的缺点。 第一种选择是不可移植的。 使用__file__更具可移植性,但是如果安装了Python项目,则它可能最终位于zip内并且没有__file__属性。 第三种选择解决了这个问题,但是很慢。

A better solution is the new importlib.resources module in the standard library. It uses Python’s existing import functionality to also import data files. Assume you have a resource inside a Python package like this:

更好的解决方案是标准库中新的importlib.resources模块。 它使用Python现有的导入功能来导入数据文件。 假设您在Python包中有这样的资源:

Note that data needs to be a Python package. That is, the directory needs to contain an __init__.py file (which may be empty). You can then read the alice_in_wonderland.txt file as follows:

请注意, data必须是Python包 。 也就是说,该目录需要包含__init__.py文件(该文件可能为空)。 然后,您可以阅读alice_in_wonderland.txt文件,如下所示:

 >>> >>>  from from importlib importlib import import resources
resources
>>> >>>  with with resourcesresources .. open_textopen_text (( "data""data" , , "alice_in_wonderland.txt""alice_in_wonderland.txt" ) ) as as fidfid :
:
...     ...     alice alice = = fidfid .. readlinesreadlines ()
()
... 
... 
>>> >>>  printprint (( """" .. joinjoin (( alicealice [:[: 77 ]))
]))
CHAPTER I. Down the Rabbit-Hole

CHAPTER I. Down the Rabbit-Hole

Alice was beginning to get very tired of sitting by her sister on the
Alice was beginning to get very tired of sitting by her sister on the
bank, and of having nothing to do: once or twice she had peeped into the
bank, and of having nothing to do: once or twice she had peeped into the
book her sister was reading, but it had no pictures or conversations in
book her sister was reading, but it had no pictures or conversations in
it, ‘and what is the use of a book,’ thought Alice ‘without pictures or
it, ‘and what is the use of a book,’ thought Alice ‘without pictures or
conversations?’
conversations?’

A similar resources.open_binary() function is available for opening files in binary mode. In the earlier “plugins as module attributes” example, we used importlib.resources to discover the available plugins using resources.contents(). See Barry Warsaw’s PyCon 2018 talk for more information.

类似的resources.open_binary()函数可用于以二进制模式打开文件。 在前面的“作为模块属性的插件”示例中 ,我们使用importlib.resources使用resources.contents()发现可用的插件。 有关更多信息,请参见Barry Warsaw的PyCon 2018演讲

It is possible to use importlib.resources in Python 2.7 and Python 3.4+ through a backport. A guide on migrating from pkg_resources to importlib.resources is available.

通过backport可以在Python 2.7和Python 3.4+中使用importlib.resources 。 一个从迁移指南pkg_resourcesimportlib.resources可用。

开发人员技巧 (Developer Tricks)

Python 3.7 has added several features aimed at you as a developer. You have already seen the new breakpoint() built-in. In addition, a few new -X command line options have been added to the Python interpreter.

Python 3.7添加了一些针对您作为开发人员的功能。 您已经看到了内置的新breakpoint() 。 另外,一些新的-X命令行选项已添加到Python解释器中。

You can easily get an idea of how much time the imports in your script takes, using -X importtime:

您可以使用-X importtime轻松了解脚本中的导入需要花费多少时间:

The cumulative column shows the cumulative time of import (in microseconds). In this example, importing plugins took about 0.03 seconds, most of which was spent importing importlib.resources. The self column shows the import time excluding nested imports.

cumulative列显示了累积的导入时间(以微秒为单位)。 在此示例中,导入plugins花费了大约0.03秒,其中大部分时间用于导入importlib.resourcesself列显示导入时间,不包括嵌套导入。

You can now use -X dev to activate “development mode.” The development mode will add certain debug features and runtime checks that are considered too slow to be enabled by default. These include enabling faulthandler to show a traceback on serious crashes, as well as more warnings and debug hooks.

现在,您可以使用-X dev激活“开发模式”。 开发模式将添加某些调试功能和运行时检查,这些功能和运行时检查被认为太慢而无法默认启用。 其中包括使faulthandler在严重崩溃时显示回溯,以及更多警告和调试挂钩。

Finally, -X utf8 enables UTF-8 mode. (See PEP 540.) In this mode, UTF-8 will be used for text encoding regardless of the current locale.

最后, -X utf8启用UTF-8模式 。 (请参阅PEP540 。)在此模式下,无论当前语言环境如何, UTF-8都将用于文本编码。

最佳化 (Optimizations)

Each new release of Python comes with a set of optimizations. In Python 3.7, there are some significant speed-ups, including:

Python的每个新发行版都有一组优化。 在Python 3.7中,有一些显着的提速,包括:

  • There is less overhead in calling many methods in the standard library.
  • Method calls are up to 20% faster in general.
  • The startup time of Python itself is reduced by 10-30%.
  • Importing typing is 7 times faster.
  • 在标准库中调用许多方法的开销较小。
  • 通常,方法调用速度最多可提高20%。
  • Python本身的启动时间减少了10-30%。
  • 导入typing速度快7倍。

In addition, many more specialized optimizations are included. See this list for a detailed overview.

此外,还包括许多更专业的优化。 请参阅此列表以获取详细概述。

The upshot of all these optimizations is that Python 3.7 is fast. It is simply the fastest version of CPython released so far.

所有这些优化的结果是Python 3.7速度很快 。 它只是到目前为止发布的最快的CPython版本

那么,我应该升级吗? (So, Should I Upgrade?)

Let’s start with the simple answer. If you want to try out any of the new features you have seen here, then you do need to be able to use Python 3.7. Using tools such as pyenv or Anaconda makes it easy to have several versions of Python installed side by side. There is no downside to installing Python 3.7 and trying it out.

让我们从简单的答案开始。 如果您想尝试这里看到的任何新功能,那么您确实需要能够使用Python 3.7。 使用pyenvAnaconda之类的工具可以轻松地并排安装多个版本的Python。 安装Python 3.7并尝试它没有任何缺点。

Now, for the more complicated questions. Should you upgrade your production environment to Python 3.7? Should you make your own project dependent on Python 3.7 to take advantage of the new features?

现在,对于更复杂的问题。 您是否应该将生产环境升级到Python 3.7? 您是否应该使自己的项目依赖于Python 3.7来利用这些新功能?

With the obvious caveat that you should always do thorough testing before upgrading your production environment, there are very few things in Python 3.7 that will break earlier code (async and await becoming keywords is one example though). If you are already using a modern Python, upgrading to 3.7 should be quite smooth. If you want to be a little conservative, you might want to wait for the release of the first maintenance release—Python 3.7.1—tentatively expected some time in July 2018.

显而易见的警告是,在升级生产环境之前,应始终进行全面的测试,Python 3.7中很少有东西会破坏早期的代码( asyncawait成为关键字是一个示例)。 如果您已经在使用现代Python,则升级到3.7应该很顺利。 如果您想稍微保守一些,则可能要等待第一个维护版本(Python 3.7.1)的发布, 暂定在2018年7月的某个时候发布。

Arguing that you should make your project 3.7 only is harder. Many of the new features in Python 3.7 are either available as backports to Python 3.6 (data classes, importlib.resources) or conveniences (faster startup and method calls, easier debugging, and -X options). The latter, you can take advantage of by running Python 3.7 yourself while keeping your code compatible with Python 3.6 (or lower).

认为只应使项目3.7困难。 Python 3.7中的许多新功能都可以作为Python 3.6的importlib.resources移植(数据类, importlib.resources )或便利性(更快的启动和方法调用,更容易的调试以及-X选项)。 后者,您可以通过自己运行Python 3.7来利用,同时保持您的代码与Python 3.6(或更低版本)兼容。

The big features that will lock your code to Python 3.7 are __getattr__() on modules, forward references in type hints, and the nanosecond time functions. If you really need any of these, you should go ahead and bump your requirements. Otherwise, your project will probably be more useful to others if it can be run on Python 3.6 for a while longer.

将代码锁定到Python 3.7的主要功能是模块上的__getattr__() ,类型提示中的正向引用以及纳秒级time函数 。 如果您确实需要其中任何一个,则应继续改进您的要求。 否则,如果可以在Python 3.6上运行更长的时间,您的项目可能会对其他人更有用。

See the Porting to Python 3.7 guide for details to be aware of when upgrading.

有关升级时要注意的详细信息,请参见《 移植到Python 3.7指南 》。

翻译自: https://www.pybloggers.com/2018/06/cool-new-features-in-python-3-7/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值