Python 2  -->  Python 3

1. 使用__future__模块

    Python 3.X引入了一些与Python 2.X不兼容的关键字和特性。在Python 2.X中,可以通过内置的__future__模块导入这些新内容。如果你希望在Python 2.X环境下写的代码也可以在Python 3.X中运行,那么建议使用__future__模块。

2. print函数

虽然print语法是Python 3中一个很小的改动,而且应该已经广为人知,但是依然值得一提:

    Python 2中的print语句被Python 3中的print()函数代替,这意味着在Python 3中必须依然用括号将需要输出的对象括起来。在Python 2中使用额外的括号也可以,但是如果在Python 3中以Python 2的形式不带括号调用print函数,就会触发SyntaxError(语法错误)。

3. 整数除法

  由于人们常常会忽视Python 3在整数除法上的改动(写错了也不会除法SyntaxError),因此在移植代码或在Python 2中执行Python 3的代码时需要特别注意这个改动。

4. Unicode

  Python 2有基于ASCII的str()类型, 可通过单独的unicode()函数转成unicode类型,但没有byte类型。在Python 3中有了Unicode (UTF-8)字符串和两个字节类(bytes 和 bytearrays)。

5. xrange

  在Python 2.X中,经常会用xrange()创建一个可迭代对象,通常出现在“for循环”或“列表/集合/字典推导式”中。在Python 3中,range()的实现方式与xrange()函数相同,所以不存在专用的xrange()(在Python 3中使用xrange()会触发NameError)。

6. 触发异常

  Python 2支持新旧两种异常除法语法,而Python 3只支持带括号的语法(不然会触发SyntaxError)。

7.处理异常

  Python 3中的异常处理发生了一点变化。在Python 3中必须使用as关键字,Python 2中不需要。

8.next()函数和.next()方法

  由于会经常用到next()函数(.next()方法)——返回迭代器的下一个项目。,因此要提到一个语法改动(实现方面也做了改动);在Python 2中,函数形式和方法形式都可以使用;在Python 3中,只能使用next()函数(试图调用.next()方法会触发AttributeError——python找不到对应的对象的属性)。

9.for循环变量与全局命名空间泄漏

  在Python 3.X中,for循环中的变量不再会泄漏到全局命名空间中了。Python中循环引用导致内存泄漏的

10.比较无序类型

  Python 3中另一个优秀的改动是,如果我们试图比较无序类型,就会触发一个TypeError。

11.使用input()解析输入内容

  Python 3改进了input()函数,这样该函数就会总是将用户的输入存储为str对象。在Python 2中,为了避免读取费字符串类型会发生的一些危险行为,不得不使用raw_input()代替input()。

12.返回可迭代对象,而不是列表

  某些函数和方法在Python 3中返回的是可迭代对象,而不像在Python 2中返回列表。对象只遍历一次,会节省很多内存,如果通过生成器多次迭代这些对象,效率就不高了。此时如果需要列表对象,可以通过Python 3的list()函数简单地将可迭代对象转换成列表。

Python 3.6  -->  Python 3.7

PEP 538, 默认解释器的命令行接口将通过一个新的环境变量PYTHONCOERCECLOCALE自动在有可行的基于UTF-8的环境(如C.UTF-8,C.utf8和 UTF-8)时转换,取决于环境变量是否设置和其值。

PEP 539, 为CPython带来了一个针对本地线程存储的新C-API。PEP 540, 加入了一个新的UTF-8模式,默认在POSIX兼容的操作系统上开启,可通过环境变量PYTHONUTF8 更改。

PEP 552, 为pyc格式文件新增了一个基于哈希值的源码文件验证。

PEP 553, 一个新增的内置函数breakpoint()使你可以更方便的进入Python或自定义Debugger。

PEP 557, 一个新装饰器Data Classes, 减少创建类时所需要的代码量,并可自动创建__init__,__repr__, 和 __eq__ 等方法。

PEP 560, 通过修改解释器核心部分优化了typing模块和泛型类型。

PEP 562, 对模块属性的自定义访问。

PEP 563, 通过推迟对注释语句的分析从而优化Python的类型提示。

PEP 564, 为time模块新增支持返回int整数形式的纳秒精度方法。

PEP 565, 再一次默认在 __main__ 处显示Deprecation Warning如SyntaxWarning, RuntimeWarning。

PEP 567,新增contextvars模块,通过上下文管理器以防止变量的状态在并发代码中意外泄漏到其它代码。

我们最需要注意的是 相对于 3.6 来说, 3.7 在语法上几乎是完全 兼容, 除了如下这一点:

1 Backwards incompatible syntax changes:
2 async and await are now reserved keywords.

这段话,指出了 3.7 的唯一不兼容 3.6 的地方就是 async 和 await 变成了Python语言中保留的关键字了。

什么意思?

就是async和await这两个词就像 import, for, while这些词一样, 是系统保留特殊用途的关键字。

也就是说在Python 3.7 里面你不能使用定义变量、函数之类的名字。

Python 3.7  -->  Python 3.8

赋值表达式

 

Python 3.8最明显的变化就是赋值表达式,即:=操作符。赋值表达式可以讲一个值赋给一个变量,即使变量不存在也可以。它可以用在表达式中,无需作为单独的语句出现。

1 while (line := file.readline()) != "end":    print(chunk)"end":
2     print(chunk)

上例中,如果变量line不存在则会被创建,然后将file.readline()的返回值赋给它。然后检查line是否为"end"。如果不是,则读取下一行,保存在line中,然后继续测试。

赋值表达式遵循了Python一贯简洁的传统,就像列表解析式一样。其目的在于避免在特定的Python编程模式中出现一些枯燥的样板代码。例如,上述代码用一般写法需要多写两行代码。

仅通过位置指定的参数

 

仅通过位置指定的参数是函数定义中的一个新语法,可以让程序员强迫某个参数只能通过位置来指定。这样可以解决Python函数定义中哪个参数是位置参数、哪个参数是关键字参数的模糊性。

仅通过位置指定的参数可以用于如下情况:某个函数接受任意关键字参数,但也能接受一个或多个未知参数。Python的内置函数通常都是这种情况,所以允许程序员这样做,能增强Python语言的一致性。

Python文档中给出的例子如下:

1 def pow(x, y, z=None, /):    r = x**y    if z is not None:        r %= z    return r
2     r = x**y
3     if z is not None:
4         r %= z
5     return r

符号 / 分隔了位置参数和关键字参数。在这个例子中,所有参数都是未知参数。在以前版本的Python中,z会被认为是关键字参数。但采用上述函数定义,pow(2, 10)和pow(2, 10, 5)都是正确的调用方式,而pow(2, 10, z=5)是不正确的。

 

支持f字符串调试

 

f字符串格式可以更方便地在同一个表达式内进行输出文本和值或变量的计算,而且效率更高。

1 x = 3 print(f'{x+1}')
2 print(f'{x+1}')

输出4。

在f字符串表达式末未添加=可以输出f表达式本身的值,后面是计算后的值。

1 x = 3print (f'{x+1=}')
2 print (f'{x+1=}')

输出为x+1=4。

 

多进程共享内存

 

在Python 3.8中,multiprocessing模块提供了SharedMemory类,可以在不同的Python进城之间创建共享的内存区域。

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

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

 

Typing模块的改进

 

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

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

 

新版本的pickle协议

 

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

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上安装。

 

可反转字典

 

Python3.6中重写了字典,其使用了PyPy项目贡献的一个新实现。除了更快、更紧凑之外,现在的字典还会继承元素的顺序——元素会按照添加的顺序排列,就像列表一样。Python 3.8还允许在字典上使用reversed()。

 

性能改进

 

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

 

Python C API和CPython实现

 

Python最近的版本在CPython(C语言编写的Python的参考实现)中使用的C API重构方面下了很大功夫。到目前为止这些工作还在不断添加,现有的成果包括:

  • Python初始化配置(Python Initialization Configuration)有了个新的C API,可以实现对Python初始化例程更紧密的控制和更详细的反馈。如此一来,将Python运行时嵌入到其他应用程序中就会更容易,也可以以编程方式给Python程序传递启动参数。新的API还确保了所有Python配置控制都有一个单一的、一致的位置,因此以后的改变(如Python的新的UTF-8模式)也更为容易。
  • CPython的另一个新的C API——"vectorcall"调用协议——可以实现针对Python内部方法更快的调用,而无需创建临时对象。该API依然不稳定,但已有了明显的改善。该API计划在Python 3.9中成熟。
  • Python运行时的审计钩子为Python运行时提供了两个API,可以用来勾住事件,从而保证测试框架、日志和审计系统等外部工具能够监视到它们。