今年六月底的时候,经过多轮测试和评估审核,Python 官方终于发布了 Python3.7 的正式版,增强了多处特性功能。本次更新,主要是为了解决编程过程中遇到的难点和痛点,所以我们应该多多学习和关注。

1. 主要特性
新版本中共新增了9个 PEP 标准,并没有特别惊艳的改变,下面主要介绍一些语法和写法上的新变化。
- PEP 539
- 新增
CPython中用于线程本地存储的C-API
- 新增
- PEP 545
- 官方文档中新增日文、法文、韩文翻译版本,就是没有中文
- PEP 552
- 优化
pyc文件
- 优化
- PEP 553
- 新增内置函数
breakpoint(),该函数在调用时自动进入调试器
- 新增内置函数
- PEP 557
- 新增内置模块
dataclasses,可大幅简化类实例属性的初始化定义
- 新增内置模块
- PEP 560
- 新增支持类型模块和泛型
- PEP 562
- 支持在模块定义
__getattr__和__dir__
- 支持在模块定义
- PEP 563
- 推迟对注释语句的分析从而优化
Python的类型提示
- 推迟对注释语句的分析从而优化
- PEP 564
time内置函数支持纳秒
- PEP 565
- 重新在
__main__中默认显示DeprecationWarning
- 重新在
- PEP 567
- 新增
contextvars模块,可实现上下文变量解决变量线程安全
- 新增
另外还有三处发生了变动,**[1] 避免使用 ASCII 作为默认文本编码,强制 UTF-8 编码运行、[2] 字典对象的 keys 按插入顺序排列,现在是官方语言规范、[3] 多方面的显著性能优化**,可见对应该版本主要是对编程中的痛点和性能做了优化和改进。
2. 特性详解
- PEP-553 内置断点支持
以前使用 pdb 的时候,需要导入pdb模块(import pdb)之后设置 pdb.set_trace(),现在使用内置的 breakpoint() 函数可以快速中断。同时,breakpoint() 可以使用任意的调试工具,这也方便了在调试代码时的扩展。你可以通过 PYTHONBREAKPOINT=0 环境变量禁用这个功能。
- PEP-562 限制访问模块的属性
我们之前在编写 Python 模块时,通常无法限制使用者使用内部的属性,比较典型的例子是在管理失效警告时。新的语法通过对模块添加 __getattr__ 和 __dir__,可以实现限制访问/提醒失效目的。
假如需要失效部分方法时,我们需要在使用之前进行提醒,这里可以使用 __getattr__ d 方法对其进行操作。
# lib.py
from warnings import warn
deprecated_names = ["old_function", ...]
def _deprecated_old_function(arg, other):
...
def __getattr__(name):
if name in deprecated_names:
warn(f"{name} is deprecated", DeprecationWarning)
return globals()[f"_deprecated_{name}"]
raise AttributeError(f"module {__name__} has no attribute {name}")
# main.py
from lib import old_function
# Works, but emits the warning
同样,__dir__也可以用在类似的用途上。
# lib.py
deprecated_names = ['old_function', ...]
__all__ = ['new_function_one', 'new_function_two', ...]
def new_function_one(arg, other):
...
def new_function_two(arg, other):
...
def __dir__():
return sorted(__all__ + deprecated_names)
# main.py
import lib
dir(lib)
# ['new_function_one', 'new_function_two', 'old_function', ...]
- PEP-564 支持 nanosecond 的时间函数
为了方便对 nanosecond 的操作,提供了 6 个新增的函数,分别为 clock_gettime_ns()、 clock_settime_ns()、 monotonic_ns()、 perf_counter_ns()、 process_time_ns() 和 time_ns()。基本上是在原有函数的基础上添加 _ns 结尾标记,用法也基本相同。
- PEP-557 数据类装饰器
在新版本中添加了@dataclass装饰器,利用该装饰器可以减少数据类型定义的代码行数。这个特性可能是 3.7.0 以后比较常用的了,是从其他语言借鉴过来的,这里简单演示下用法。
假如,我们要封装一个类对象,在之前我们的代码可能要这么写。
class Article(object):
def __init__(self, _id, author_id, title, text, tags=None,
created=datetime.now(), edited=datetime.now()):
self._id = _id
self.author_id = author_id
self.title = title
self.text = text
self.tags = list() if tags is None else tags
self.created = created
self.edited = edited
if type(self.created) is str:
self.created = dateutil.parser.parse(self.created)
if type(self.edited) is str:
self.edited = dateutil.parser.parse(self.edited)
def __eq__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
return (self._id, self.author_id) == (other._id, other.author_id)
def __lt__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
return (self._id, self.author_id) < (other._id, other.author_id)
def __repr__(self):
return '{}(id={}, author_id={}, title={})'.format(
self.__class__.__name__, self._id, self.author_id, self.title)
大量的初始化属性要定义默认值,可能还需要重写一堆魔法方法,来实现类实例之间的排序、去重等功能。如果使用dataclass进行改造,可以写成这个样子。这种语法使代码更加简练清晰,也更符合面向对象思想的语法方式,用过 SQLAlchemy 的同学肯定觉得很像 ORM 写法。
@dataclass(order=True)
class Article(object):
_id: int
author_id: int
title: str = field(compare=False)
text: str = field(repr=False, compare=False)
tags: List[str] = field(default=list(), repr=False, compare=False)
created: datetime = field(default=datetime.now(), repr=False, compare=False)
edited: datetime = field(default=datetime.now(), repr=False, compare=False)
def __post_init__(self):
if type(self.created) is str:
self.created = dateutil.parser.parse(self.created)
if type(self.edited) is str:
self.edited = dateutil.parser.parse(self.edited)
3. 扩展补充
- 性能改进
部分Python3性能都进行了提升,不过都不如关于函数调用的改进最为明显。通过添加LOAD_METHOD和CALL_METHOD两个opcode,函数调用进行了较大提升。基本上对于2.7版本而言,大部分的性能已经超过,只有少数指标仍旧落后于Python2.7。
- 其他内容
其他的内容,PEP-538 和 PEP-540 均是关于 Python 对于 UTF-8 环境的支持,其中 538 主要影响了 *nix 系统下的 UTF-8 字符串显示,PEP-540 主要是扩大了默认 UTF-8 编码的范围,比如 sys.getfilesystemencoding() 等将会返回 UTF-8 。这些问题一般用户并不会遇到,因此我这里不做更多描述,有兴趣的可以去看下具体的内容即可。PEP-552 扩展了 pyc 文件的格式,更好的支持 pyc 缓存,感觉作用不大,这里也不做太多介绍。PEP-539 定义了一套新的 Thread Soecific Storage API,提升对跨平台的支持性,这里也不做介绍了。