今年六月底的时候,经过多轮测试和评估审核,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
,提升对跨平台的支持性,这里也不做介绍了。