python3.7新功能_Python 3.7 新特性概览(附实例

目录

许多人使用的第一种、被称为世界上增长最快的编程语言当然是 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 的到来!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值