你应该知道的Python3.6、3.7、3.8新特性小结


Posted in Python onMay 12, 2020

很多人在学习了基本的Python语言知识后,就转入应用阶段了,后期很少对语言本身的新变化、新内容进行跟踪学习和知识更新,甚至连已经发布了好几年的Python3.6的新特性都缺乏了解。

本文列举了Python3.6、3.7、3.8三个版本的新特性,学习它们有助于提高对Python的了解,跟上最新的潮流。

一、Python3.6新特性

1、新的格式化字符串方式

新的格式化字符串方式,即在普通字符串前添加 f 或 F 前缀,其效果类似于str.format()。比如

name = "red"
print(f"He said his name is {name}.") 
# 'He said his name is red.'

相当于:

print("He said his name is {name}.".format(**locals()))

此外,此特性还支持嵌套字段,比如:

import decimal
width = 10
precision = 4
value = decimal.Decimal("12.34567")
print(f"result: {value:{width}.{precision}}") 
#'result: 12.35'

2、变量声明语法

可以像下面一样声明一个变量并指定类型:

from typing import List, Dict
 
primes: List[int] = []
captain: str # 此时没有初始值
 
class Starship:
 stats: Dict[str, int] = {}

3、数字的下划线写法

允许在数字中使用下划线,以提高多位数字的可读性。

a = 1_000_000_000_000_000  # 1000000000000000
b = 0x_FF_FF_FF_FF    # 4294967295

除此之外,字符串格式化也支持_选项,以打印出更易读的数字字符串:

'{:_}'.format(1000000)   # '1_000_000'
'{:_x}'.format(0xFFFFFFFF)  # 'ffff_ffff'

4、异步生成器

在Python3.5中,引入了新的语法 async 和 await 来实现协同程序。但是有个限制,不能在同一个函数体内同时使用 yield 和 await。Python3.6中,这个限制被放开了,允许定义异步生成器:

async def ticker(delay, to):
"""Yield numbers from 0 to *to* every *delay* seconds."""
 for i in range(to):
  yield i
  await asyncio.sleep(delay)

5、异步解析器

允许在列表list、集合set 和字典dict 解析器中使用 async 或 await 语法。

result = [i async for i in aiter() if i % 2]
result = [await fun() for fun in funcs if await condition()]

6、新增加模块

标准库(The Standard Library)中增加了一个新的模块:secrets。该模块用来生成一些安全性更高的随机数,用于管理passwords, account authentication, security tokens, 以及related secrets等数据。

7、其他新特性

  • 新的 PYTHONMALLOC 环境变量允许开发者设置内存分配器,以及注册debug钩子等。
  • asyncio模块更加稳定、高效,并且不再是临时模块,其中的API也都是稳定版的了。
  • typing模块也有了一定改进,并且不再是临时模块。
  • datetime.strftime 和 date.strftime 开始支持ISO 8601的时间标识符%G, %u, %V。
  • hashlib 和 ssl 模块开始支持OpenSSL1.1.0。
  • hashlib模块开始支持新的hash算法,比如BLAKE2, SHA-3 和 SHAKE。
  • Windows上的 filesystem 和 console 默认编码改为UTF-8。
  • json模块中的 json.load() 和 json.loads() 函数开始支持 binary 类型输入。

更多内容参考官方文档:What's New In Python 3.6

二、Python3.7新特性

Python 3.7于2018年6月27日发布, 包含许多新特性和优化,增添了众多新的类,可用于数据处理、针对脚本编译和垃圾收集的优化以及更快的异步I/O,主要如下:

  • 用类处理数据时减少样板代码的数据类。
  • 一处可能无法向后兼容的变更涉及处理生成器中的异常。
  • 面向解释器的“开发模式”。
  • 具有纳秒分辨率的时间对象。
  • 环境中默认使用UTF-8编码的UTF-8模式。
  • 触发调试器的一个新的内置函数。

1、新增内置函数breakpoint()

使用该内置函数,相当于通过代码的方式设置了断点,会自动进入Pbd调试模式。

如果在环境变量中设置PYTHONBREAKPOINT=0会忽略此函数。并且,pdb 只是众多可用调试器之一,你可以通过设置新的 PYTHONBREAKPOINT 环境变量来配置想要使用的调试器。

下面有一个简单例子,用户需要输入一个数字,判断它是否和目标数字一样:

"""猜数字游戏"""

def guess(target):
  user_guess = input("请输入你猜的数 >>> ")
  if user_guess == target:
    return "你猜对了!"
  else:
    return "猜错了"


if __name__ == '__main__':
  a = 100
  print(guess(a))

不幸的是,即使猜的数和目标数一样,打印的结果也是‘猜错了',并且没有任何异常或错误信息。

为了弄清楚发生了什么,我们可以插入一个断点,来调试一下。以往一般通过print大法或者IDE的调试工具,但现在我们可以使用 breakpoint()。

"""猜数字游戏"""


def guess(target):
  user_guess = input("请输入你猜的数 >>> ")
  breakpoint()  //加入这一行
  if user_guess == target:
    return "你猜对了!"
  else:
    return "猜错了"


if __name__ == '__main__':
  a = 100
  print(guess(a))

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

请输入你猜的数 >>> 100
> d:\work\for_test\py3_test\test.py(7)guess()
-> if user_guess == target:
(Pdb) locals()
{'target': 100, 'user_guess': '100'}
(Pdb) type(user_guess)
<class 'str'>

搞明白了,target是一个整数,而user_guess 是一个字符串,这里发生了类型对比错误。

2、类型和注解

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

什么是注解?它们是关联元数据与变量的语法支持,可以是任意表达式,在运行时被 Python 计算但被忽略。注解可以是任何有效的 Python 表达式。

下面是个对比的例子:

# 不带类型注解
def foo(bar, baz):
# 带类型注解
def foo(bar: 'Describe the bar', baz: print('random')) -> 'return thingy':

上面的做法,其实是Python对自身弱类型语言的强化,希望获得一定的类型可靠和健壮度,向Java等语言靠拢。

在 Python 3.5 中,注解的语法获得标准化,此后,Python 社区广泛使用了注解类型提示。

但是,注解仅仅是一种开发工具,可以使用 PyCharm 等 IDE 或 Mypy 等第三方工具进行检查,并不是语法层面的限制。

我们前面的猜数程序如果添加类型注解,它应该是这样的:

"""猜数字游戏"""


def guess(target:str):
  user_guess:str = input("请输入你猜的数 >>> ")
  breakpoint()
  if user_guess == target:
    return "你猜对了!"
  else:
    return "猜错了"


if __name__ == '__main__':
  a:int = 100
  print(guess(a))

PyCharm会给我们灰色的规范错误提醒,但不会给红色的语法错误提示。

用注解作为类型提示时,有两个主要问题:启动性能和前向引用。

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

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

而对于向前引用,看下面的例子:

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

错误在于 User类型还没有被声明,此时的 prev_user 不能定义为 User 类型。

为了解决这个问题,Python3.7 将注解的评估进行了推迟。并且,这项改动向后不兼容,需要先导入annotations,只有到Python 4.0后才会成为默认行为。

from __future__ import annotations

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

或者如下面的例子:

class C:
  def validate_b(self, obj: B) -> bool:
    ...
class B:
  ...

3、新增dataclasses模块

这个特性可能是 Python3.7以后比较常用的,它有什么作用呢?

假如我们需要编写一个下面的类:

from datetime import datetime
import dateutil

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)

大量的初始化属性要定义默认值,可能还需要重写一堆魔法方法,来实现类实例的打印、比较、排序和去重等功能。

如果使用dataclasses进行改造,可以写成这个样子:

from dataclasses import dataclass, field
from typing import List
from datetime import datetime
import dateutil

@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)

这使得类不仅容易设置,而且当我们创建一个实例并打印出来时,它还可以自动生成优美的字符串。在与其他类实例进行比较时,它也会有适当的行为。这是因为dataclasses除了帮我们自动生成 __init__ 方法外,还生成了一些其他特殊方法,如 repr、eq 和 hash 等。

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

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

4、生成器异常处理

在Python 3.7中,生成器引发StopIteration异常后,StopIteration异常将被转换成RuntimeError异常,那样它不会悄悄一路影响应用程序的堆栈框架。这意味着如何处理生成器的行为方面不太敏锐的一些程序会在Python 3.7中抛出RuntimeError。在Python 3.6中,这种行为生成一个弃用警告;在Python 3.7中,它将生成一个完整的错误。

一个简易的方法是使用try/except代码段,在StopIteration传播到生成器的外面捕获它。更好的解决方案是重新考虑如何构建生成器??比如说,使用return语句来终止生成器,而不是手动引发StopIteration。

5、开发模式

Python解释器添加了一个新的命令行开关:-X,让开发人员可以为解释器设置许多低级选项。

这种运行时的检查机制通常对性能有重大影响,但在调试过程中对开发人员很有用。

-X 激活的选项包括:

  • asyncio模块的调试模式。这为异步操作提供了更详细的日志记录和异常处理,而异常操作可能很难调试或推理。
  • 面向内存分配器的调试钩子。这对于编写CPython扩展件的那些人很有用。它能够实现更明确的运行时检查,了解CPython如何在内部分配内存和释放内存。
  • 启用faulthandler模块,那样发生崩溃后,traceback始终转储出去。

6、 高精度时间函数

Python 3.7中一类新的时间函数返回纳秒精度的时间值。尽管Python是一种解释型语言,但是Python的核心开发人员维克多•斯廷纳(Victor Stinner)主张报告纳秒精度的时间。最主要的原因是,在处理转换其他程序(比如数据库)记录的时间值时,可以避免丢失精度。

新的时间函数使用后缀_ns。比如说,time.process_time()的纳秒版本是time.process_time_ns()。请注意,并非所有的时间函数都有对应的纳秒版本。

7、其他新特性

  • 字典现在保持插入顺序。这在 3.6 中是非正式的,但现在成为了官方语言规范。在大多数情况下,普通的 dict 能够替换 collections.OrderedDict。
  • .pyc 文件具有确定性,支持可重复构建 —— 也就是说,总是为相同的输入文件生成相同的 byte-for-byte 输出。
  • 新增contextvars模块,针对异步任务提供上下文变量。
  • __main__中的代码会显示弃用警告(DeprecationWarning)。
  • 新增UTF-8模式。在Linux/Unix系统,将忽略系统的locale,使用UTF-8作为默认编码。在非Linux/Unix系统,需要使用-X utf8选项启用UTF-8模式。
  • 允许模块定义__getattr__、__dir__函数,为弃用警告、延迟import子模块等提供便利。
  • 新的线程本地存储C语言API。
  • 更新Unicode数据到11.0。

三、Python3.8新特性

Python3.8版本于2019年10月14日发布,以下是 Python 3.8 相比 3.7 的新增特性。

1、海象赋值表达式

新的语法 :=,将值赋给一个更大的表达式中的变量。它被亲切地称为 “海象运算符”(walrus operator),因为它长得像海象的眼睛和象牙。

“海象运算符” 在某些时候可以让你的代码更整洁,比如:

在下面的示例中,赋值表达式可以避免调用 len () 两次:

if (n := len(a)) > 10:  
  print(f"List is too long ({n} elements, expected <= 10)")

类似的好处还可体现在正则表达式匹配中需要使用两次匹配对象的情况中,一次检测用于匹配是否发生,另一次用于提取子分组:

discount = 0.0  

if (mo := re.search(r'(\d+)% discount', advertisement)):  
  discount = float(mo.group(1)) / 100.0

此运算符也可用于配合 while 循环计算一个值,来检测循环是否终止,而同一个值又在循环体中再次被使用的情况:

# Loop over fixed length blocks  

while (block := f.read(256)) != '':  
  process(block)

或者出现于列表推导式中,在筛选条件中计算一个值,而同一个值又在表达式中需要被使用:

[clean_name.title() for name in names  

 if (clean_name := normalize('NFC', name)) in allowed_names]

请尽量将海象运算符的使用限制在清晰的场合中,以降低复杂性并提升可读性。

2、仅限位置形参

新增一个函数形参语法 / 用来指明某些函数形参必须使用仅限位置而非关键字参数的形式。

这种标记语法与通过 help () 所显示的使用 Larry Hastings 的 Argument Clinic 工具标记的 C 函数相同。

在下面的例子中,形参 a 和 b 为仅限位置形参,c 或 d 可以是位置形参或关键字形参,而 e 或 f 要求为关键字形参:

def f(a, b, /, c, d, *, e, f):  

  print(a, b, c, d, e, f)

以下是合法的调用:

f(10, 20, 30, d=40, e=50, f=60)

但是,以下均为不合法的调用:

f(10, b=20, c=30, d=40, e=50, f=60)  # b 不可以是一个关键字参数
f(10, 20, 30, 40, 50, f=60)      # e 必须是一个关键字参数

这种标记形式的一个用例是它允许纯 Python 函数完整模拟现有的用 C 代码编写的函数的行为。例如,内置的 pow () 函数不接受关键字参数:

def pow(x, y, z=None, /):  

  "Emulate the built in pow() function"  

  r = x ** y  

  return r if z is None else r%z

另一个用例是在不需要形参名称时排除关键字参数。例如,内置的 len () 函数的签名为 len (obj, /)。这可以排除如下这种笨拙的调用形式:

len(obj='hello') # The "obj" keyword argument impairs readability

另一个益处是将形参标记为仅限位置形参将允许在未来修改形参名而不会破坏客户的代码。例如,在 statistics 模块中,形参名 dist 在未来可能被修改。这使得以下函数描述成为可能:

def quantiles(dist, /, *, n=4, method='exclusive')  
  ...

由于在 / 左侧的形参不会被公开为可用关键字,其他形参名仍可在 **kwargs 中使用:

>>> def f(a, b, /, **kwargs):  
...   print(a, b, kwargs)  
...  
>>> f(10, 20, a=1, b=2, c=3)     # a and b are used in two ways  
10 20 {'a': 1, 'b': 2, 'c': 3}

这极大地简化了需要接受任意关键字参数的函数和方法的实现。例如,下面是 collections 模块中的代码摘录:

class Counter(dict):  

  def __init__(self, iterable=None, /, **kwds):  

    # Note "iterable" is a possible keyword argument

3、f 字符串支持 =

增加 = 说明符用于 f-string。形式为 f'{expr=}' 的 f 字符串将扩展表示为表达式文本,加一个等于号,再加表达式的求值结果。例如:

>>> user = 'eric_idle'  
>>> member_since = date(1975, 7, 31)  
>>> f'{user=} {member_since=}'  

"user='eric_idle' member_since=datetime.date(1975, 7, 31)"
f 字符串格式说明符允许更细致地控制所要显示的表达式结果:

>>> delta = date.today() - member_since  

>>> f'{user=!s} {delta.days=:,d}'  
'user=eric_idle delta.days=16,075'
= 说明符将输出整个表达式,以便详细演示计算过程:

>>> print(f'{theta=} {cos(radians(theta))=:.3f}')  
theta=30 cos(radians(theta))=0.866

4、 typing模块的改进

Python是动态类型语言,但可以通过typing模块添加类型提示,以便第三方工具验证Python代码。Python 3.8给typing添加了一些新元素,因此它能够支持更健壮的检查:

  • final修饰器和Final类型标注表明,被修饰或被标注的对象在任何时候都不应该被重写、继承,也不能被重新赋值。
  • Literal类型将表达式限定为特定的值或值的列表(不一定是同一个类型的值)。
  • TypedDict可以用来创建字典,其特定键的值被限制在一个或多个类型上。注意这些限制仅用于编译时确定值的合法性,而不能在运行时进行限制。

5、多进程共享内存

multiprocessing模块新增SharedMemory类,可以在不同的Python进城之间创建共享的内存区域。

在旧版本的Python中,进程间共享数据只能通过写入文件、通过网络套接字发送,或采用Python的pickle模块进行序列化等方式。共享内存提供了进程间传递数据的更快的方式,从而使得Python的多处理器和多内核编程更有效率。

共享内存片段可以作为单纯的字节区域来分配,也可以作为不可修改的类似于列表的对象来分配,其中能保存数字类型、字符串、字节对象、None对象等一小部分Python对象。

6、 新版本的pickle协议

Python的pickle模块提供了一种序列化和反序列化Python数据结构或实例的方法,可以将字典原样保存下来供以后读取。不同版本的Python支持的pickle协议不同,而3.8版本的支持范围更广、更强大、更有效的序列化。

Python 3.8引入的第5版pickle协议可以用一种新方法pickle对象,它能支持Python的缓冲区协议,如bytes、memoryviews或Numpy array等。新的pickle避免了许多在pickle这些对象时的内存复制操作。

NumPy、Apache Arrow等外部库在各自的Python绑定中支持新的pickle协议。新的pickle也可以作为Python 3.6和3.7的插件使用,可以从PyPI上安装。

7、性能改进

  • 许多内置方法和函数的速度都提高了20%~50%,因为之前许多函数都需要进行不必要的参数转换。
  • 一个新的opcode缓存可以提高解释器中特定指令的速度。但是,目前实现了速度改进的只有LOAD_GLOBAL opcode,其速度提高了40%。以后的版本中也会进行类似的优化。
  • 文件复制操作如shutil.copyfile()和shutil.copytree()现在使用平台特定的调用和其他优化措施,来提高操作速度。
  • 新创建的列表现在平均比以前小了12%,这要归功于列表构造函数如果能提前知道列表长度的情况下,可以进行优化。
  • Python 3.8中向新型类(如class A(object))的类变量中的写入操作变得更快。operator.itemgetter()和collections.namedtuple()也得到了速度优化。

更多详细特性,请查阅Python 3.8.0文档:https://docs.python.org/zh-cn/3.8/whatsnew/3.8.html

到此这篇关于你应该知道的Python3.6、3.7、3.8新特性小结的文章就介绍到这了,更多相关Python3.6、3.7、3.8新特性 内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

 

Python 相关文章推荐
Python中使用第三方库xlrd来读取Excel示例
Apr 05 Python
python复制与引用用法分析
Apr 08 Python
KMP算法精解及其Python版的代码示例
Jun 01 Python
Python 数据结构之队列的实现
Jan 22 Python
Python实现中文数字转换为阿拉伯数字的方法示例
May 26 Python
Python实现PS滤镜中马赛克效果示例
Jan 20 Python
Python 中的lambda函数介绍
Oct 10 Python
python实现顺时针打印矩阵
Mar 02 Python
python正则表达式匹配IP代码实例
Dec 28 Python
python实现门限回归方式
Feb 29 Python
解决Ubuntu18中的pycharm不能调用tensorflow-gpu的问题
Sep 17 Python
Python时间操作之pytz模块使用详解
Jun 14 Python
python将logging模块封装成单独模块并实现动态切换Level方式
May 12 #Python
Python PyQt5模块实现窗口GUI界面代码实例
May 12 #Python
从0到1使用python开发一个半自动答题小程序的实现
May 12 #Python
Python列表去重复项的N种方法(实例代码)
May 12 #Python
python3中的logging记录日志实现过程及封装成类的操作
May 12 #Python
Pycharm激活方法及详细教程(详细且实用)
May 12 #Python
PyTorch在Windows环境搭建的方法步骤
May 12 #Python
You might like
PHP提取数据库内容中的图片地址并循环输出
2010/03/21 PHP
实例讲解PHP面向对象之多态
2014/08/20 PHP
php实现的简单美国商品税计算函数
2015/07/13 PHP
js静态作用域的功能。
2006/12/25 Javascript
JQuery 无废话系列教程(一) jquery入门 [推荐]
2009/06/23 Javascript
jquery选择器(常用选择器说明)
2010/09/28 Javascript
Javascript模块化编程(一)AMD规范(规范使用模块)
2013/01/17 Javascript
jQuery编辑器KindEditor4.1.4代码高亮显示设置教程
2013/03/01 Javascript
js去空格技巧分别去字符串前后、左右空格
2013/10/21 Javascript
小米公司JavaScript面试题
2014/12/29 Javascript
JQuery实现可直接编辑的表格
2015/04/16 Javascript
简单谈谈require模块化jquery和angular的问题
2017/06/23 jQuery
JS获取数组中出现次数最多及第二多元素的方法
2017/10/27 Javascript
bootstrap响应式导航条模板使用详解(含下拉菜单,弹出框)
2017/11/17 Javascript
vue实现与安卓、IOS交互的方法
2018/11/02 Javascript
Vue编写可显示周和月模式的日历 Vue自定义日历内容的显示
2019/06/26 Javascript
用python实现的可以拷贝或剪切一个文件列表中的所有文件
2009/04/30 Python
python以环状形式组合排列图片并输出的方法
2015/03/17 Python
python之PyMongo使用总结
2017/05/26 Python
python socket网络编程之粘包问题详解
2018/04/28 Python
Python爬虫实现获取动态gif格式搞笑图片的方法示例
2018/12/24 Python
python 同时运行多个程序的实例
2019/01/07 Python
Python 限制线程的最大数量的方法(Semaphore)
2019/02/22 Python
python中常见错误及解决方法
2020/06/21 Python
基于CSS3实现图片模糊过滤效果
2015/11/19 HTML / CSS
杰夫·班克斯男士服装网上商店:Jeff Banks
2019/10/24 全球购物
Huda Beauty官方商店:化妆和美容产品
2020/09/05 全球购物
社会实践自我鉴定
2013/11/07 职场文书
如何撰写岗位职责
2014/02/01 职场文书
火灾现场处置方案
2014/05/28 职场文书
清洁工岗位职责
2015/02/13 职场文书
会计求职简历自我评价
2015/03/10 职场文书
同意离婚答辩状
2015/05/22 职场文书
聘任协议书(挂靠)
2015/09/21 职场文书
使用Python的开发框架Brownie部署以太坊智能合约
2021/05/28 Python
Linux中文件的基本属性介绍
2022/06/01 Servers