Python3.6,3.7,3.8版本对⽐
本⽂列举了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 类型输⼊。
更多内容参考官⽅⽂档:
⼆、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,
w(), w()):
self._id = _id
self.author_id = author_id
self.title = title
< = text
self.tags = list() if tags is None else tags
self.edited = edited
if ated) is str:
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(w(), repr=False, compare=False)
edited: datetime = field(w(), repr=False, compare=False)
def __post_init__(self):
if ated) is str:
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传播到⽣成器的外⾯捕获它。更好的解决⽅案是重新考虑如何构建⽣成器――⽐如字符串函数title作用
说,使⽤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 = up(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]
请尽量将海象运算符的使⽤限制在清晰的场合中,以降低复杂性并提升可读性。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。