Python 3.7 新特性概览(附实例)

译自:https://hackaday.com/2018/07/23/hands-on-with-python-3-7-whats-new-in-the-latest-release/?utm_source=mybridge&utm_medium=blog&utm_campaign=read_more

目录

内置 breakpoint()

注解和类型

计时

Dataclass

其他

结论


许多人使用的第一种、被称为世界上增长最快的编程语言当然是 Python,可用于通用编程、数据科学、网站后端、GUI 以及几乎所有其他功能。最新的 3.7.0 版本 刚发布不久。

任何版本 Python 的发行,无论变化多小,在任何开发开始之前都要经过细致的规划和设计。实际上,你可以阅读 Python 3.7 的PEP (Python Enhancement Proposal,Python 增强提议),该提议是在2016年创建的。

3.7 中有什么新功能?你为什么要升级?有什么新的有用的东西吗?我将通过介绍一些新特性的例子来回答这些问题。虽然这个版本对 Python 初学者来说没有什么不同,但是对于经验丰富的程序员来说有很多小的变化,还有一些你想要了解的主要特性。


内置 breakpoint()

任何使用过 pdb (Python debugger) 的人都知道它有多么强大。它能暂停脚本的执行,允许你在程序的内部手动浏览,并且单步执行语句。

但是,到目前为止,在编写程序时需要进行一些设置。当然,导入 pdb 和 set_trace() 几乎不需要花费任何时间,但这不如插入快速调试 print() 或 log 方便。在 Python 3.7 中,breakpoint() 是内置函数,可以非常容易地在任何时候插入调试器。同样值得注意的是,pdb 只是众多可用调试器之一,你可以通过设置新的 PYTHONBREAKPOINT 环境变量来配置想要使用的调试器。

这里有一个简单例子。用户需要输入一个字符串,判断它是否匹配一个值。

"""Test user's favourite Integrated Circuit."""
 
def test_ic(favourite_ic):
    user_guess = input("Try to guess our favourite IC >>> ")
 
    if user_guess == favourite_ic:
        return "Yup, that's our favourite!"
    else:
        return "Sorry, that's not our favourite IC"
 
 
if __name__ == '__main__':
    favourite_ic = 555
    print(test_ic(favourite_ic))

不幸的是,无论输入什么,我们都无法匹配字符串。

$ python breakpoint_test.py
Try to guess our favourite IC >>> 555
Sorry, that's not our favourite IC

为了弄清楚发生了什么,让我们插入一个断点 —— 只需要调用 breakpoint()

"""Test user's favourite Integrated Circuit."""
 
def test_ic(favourite_ic):
    user_guess = input("Try to guess our favourite IC >>> ")
    breakpoint()
 
    if user_guess == favourite_ic:
        return "Yup, that's our favourite!"
    else:
        return "Sorry, that's not our favourite IC"
 
 
if __name__ == '__main__':
    favourite_ic = 555
    print(test_ic(favourite_ic))

在 pdb 提示符下,我们将调用 locals() 来调出当前的本地作用域。pdb 有大量有用的命令,但是你也可以在其中运行正常的Python 语句。

$ python breakpoint_test.py
Try to guess our favourite IC >>> 555
> /home/ben/Hackaday/python37/breakpoint_test.py(8)test_ic()
-> if user_guess == favourite_ic:
(Pdb) locals()
{'favourite_ic': 555, 'user_guess': '555'}
(Pdb)

啊哈!看起来 favourite_ic 是一个整数,而 user_guess 是一个字符串。因为在 Python 中,将字符串与 int 进行比较是完全可行的,所以没有抛出异常(但是比较没有达到我们想要的效果)。favourite_ic 应该声明为字符串,这可以说是 Python 的动态类型的危险之一 —— 在运行时之前无法捕捉到这个错误。当然,除非你使用类型注解……


注解和类型

从 Python 3.5 开始,类型注解就越来越受欢迎。对于那些不熟悉类型提示的人来说,这是一种完全可选的注释代码的方式,以指定变量的类型。

类型提示(Type hints)只是 annotations 的一个应用。注解是什么?它们是关联元数据与变量的语法支持,可以是任意表达式,在运行时被 Python 计算但被忽略。注解可以是任何有效的 Python 表达式。这里有一个带注解的函数的例子,但这个例子中使用了一些无用的信息。

# Without annotation
def foo(bar, baz):
# Annotated
def foo(bar: 'Describe the bar', baz: print('random')) -> 'return thingy':

这一切都很酷,但有点无意义,除非以标准方式使用注解。在 Python 3.5 (PEP 484)中,使用注解进行编程的语法变得标准化,此后,Python 社区广泛使用了类型提示。它们纯粹是一种开发工具,可以使用 PyCharm 等 IDE 或 Mypy 等第三方工具进行检查。

如果我们的字符串比较程序是用类型注解编写的,它应该是这样的:

"""Test user's favourite Integrated Circuit."""
 
def test_ic(favourite_ic: str) -> str:
    user_guess: str = input("Try to guess our favourite IC >>> ")
    breakpoint()
 
    if user_guess == favourite_ic:
        return "Yup, that's our favourite!"
    else:
        return "Sorry, that's not our favourite IC"
 
 
if __name__ == '__main__':
    favourite_ic: int = 555
    print(test_ic(favourite_ic))

你可以看到 PyCharm 提醒了我这个错误,它可以防止我在运行时才注意到它。

这就是注解和类型提示的基础。Python 3.7 中有什么变化?正如官方的 Python 文档所指出的,当人们开始使用注解作为类型提示时,出现了两个主要问题:启动性能和前向引用。

  • 不出意外的是,在定义时计算大量任意表达式相当影响启动性能,而且 typing 模块非常慢
  • 你不能用尚未声明的类型来注解

这种缺乏前向引用的做法似乎是合理的,但在实践中却变得相当麻烦。

class User:
    def __init__(self, name: str, prev_user: User) -> None:
        pass

这种做法将失败,因为 User 还没有被声明,因此 prev_user 不能定义为 User 类型。

为了解决这两个问题,注解的评估被推迟。

要实现上述行为,必须导入 __future__,因为在保持与以前版本兼容的情况下无法进行此更改。

from __future__ import annotations
 
class User: 
    def __init__(self, name: str, prev_user: User) -> None:
        pass

typing 模块如此缓慢的部分原因是,最初的设计目标是在不修改核心 CPython 解释器的情况下实现 typing 模块。既然类型提示变得越来越流行,这一限制已经被移除,这意味着现在有了对 typing 的核心支持,使得支持多种优化


计时

time 模块在定时上加入了一些新功能:现有的定时器功能达到了纳秒级,这意味着如果需要的话,可以有更高的精度。一些基准测试表明,time.time() 的分辨率是 time.time_ns() 的三倍以上。

说到时间,Python 本身在 3.7 中获得了一个小的速度提升。这是底层的内容,所以我们现在不深入讨论,这里有完整的优化列表。你只需要知道,在 Linux 上的启动时间比之前要快 10%,在 MacOS 上快 30%,大量的方法调用要快 20%。


Dataclass

我敢打赌,如果你曾经编写过面向对象的 Python,你就会创建一个类,最终看起来像这样:

class User:
 
    def __init__(self, name: str, age: int, favourite_ic: str) -> None:
        self.name = name
        self.age = age
        self.favourite_ic = favourite_ic
 
    def is_adult(self) -> bool:
        """Return True if user is an adult, else False."""
        return self.age >= 18
 
 
if __name__ == '__main__':
    john = User('John', 29, '555')
    print(john)
    # prints "<__main__.User object at 0x0076E610>"

当类被初始化时,__init__ 会接收到大量不同的参数。这些属性直接设置为类实例的属性,供以后使用。在编写这类类时,这是一种非常常见的模式 —— 但这是Python,如果可以避免单调乏味,那么它就可以。

在 3.7 中,我们有 dataclass,这将使这类类更容易声明,也更可读。

只需用 @dataclass 装饰类,self 的赋值就会自动处理。变量的声明如下所示,类型注解是强制性的(如果你想灵活的话,你仍然可以使用 Any 类型)。

from dataclasses import dataclass
 
@dataclass
class User:
    name: str
    age: int
    favourite_ic: str
 
    def is_adult(self) -> bool:
        """Return True if user is an adult, else False."""
        return self.age >= 18
 
if __name__ == '__main__': 
    john = User('John', 29, '555') 
    print(john) 
    # prints "User(name='John', age=29, favourite_ic='555')"

这使得类不仅容易设置,而且当我们创建一个实例并打印出来时,它还生成了一个优美的字符串。在与其他类实例进行比较时,它也会有适当的行为。这是因为,除了自动生成 __init__ 方法外,还生成了其他特殊方法,如 __repr____eq____hash__ 等。当定义这样的类时,大大减少了所需的开销。

Dataclass 使用字段 (field) 来完成它们的工作,手动构造一个 field() 函数能够访问其他选项,从而更改默认值。例如,这里将 field 中的 default_factory 设置为一个 lambda 函数,该函数提示用户输入其名称。

from dataclasses import dataclass, field
 
class User:
    name: str = field(default_factory=lambda: input("enter name"))

(我们不建议直接将 input 输入到属性中,这只是一个字段功能的演示。)


其他

在这个版本中还有许多其他的变化;我们将在这里列出一些最重要的:

  • 字典现在保持插入顺序。这在 3.6 中是非正式的,但现在是官方语言规范。在大多数情况下,普通的 dict 应该能够替换 collections.OrderedDict
  • 加入法文、日文和韩文文档翻译。
  • 对模块属性访问的控制现在更容易了,因为 __getattr__ 现在可以在模块层次进行定义。这使得定制导入行为和实现特性,例如弃用警告,变得更加容易。
  • CPython 的一种新的开发模式
  • .pyc 文件具有确定性,支持可重复构建 —— 也就是说,总是为相同的输入文件生成相同的 byte-for-byte 输出。

结论

有一些非常简洁的语法快捷方式和性能改进,但这可能不足以鼓励每个人进行升级。总的来说,Python 3.7 实现了一些特性,这些特性将真正减少混乱的代码解决方案,并生成更干净的代码。我们当然期待使用它,也等不及 3.8 的到来!

猜你喜欢

转载自blog.csdn.net/qq_20084101/article/details/81808553