Please enable Javascript to view the contents

编写 Python2、Python3 兼容的代码

 ·  ☕ 3 分钟

Python2 到 Python3 是一个较大的版本更新。目前,生产环境依然有大量项目使用的是 Python2。但,这并不意味着项目会一直停留在 Python2,开发者也需要考虑项目对 Python3 的兼容性,以方便迁移,同时也是对新知识的学习。下面是一些学习的知识点整理。

1. Python2 升级 Python3

贸然地升级 Python3 ,无疑将会面临巨大风险。充分地了解 Python2 和 Python3 的区别,学习 Python3 的新特性,预留时间,制定升级计划是必须的。

  • 单元测试。
    单元测试能跑通,是平稳升级的重要保障。单元测试能够验证升级前后,功能是否一致。如果项目没有单元测试,那么在升级之前应该补上。
  • py2 → six → py3。
    升级到 Python3,最大的难点在于,改变开发人员的使用习惯。比如,在 Python2 中推荐的 xrange, 在 Python3 中却不能用。开发人员需要一个学习和适应的过程。推荐的策略是,新的功能代码兼容 Python3,逐步重构存量代码。

2. __future__ 模块

Python 的新版本会引入新的特性,但是,实际上这些特性在上一个版本中就已经存在。要使用某一新的特性,可以通过导入 __future__ 模块来实现。

__future__ 包括下面几个新特性:

上面,表中第一列包含了所有可以从 __future__ 中导入的特性,optional in 中的版本号为最低可使用的版本,mandatory in 中的版本号为已经实现,无需从 __future__ 导入的版本号。最后一列是每个新特性所对应的 PEP 及简单描述。下面是部分 Python3 的新特性示例:

2.1 打印函数

使用 Python3 的 print 函数,禁用 Python2 的 print 语句

1
2
3
from __future__ import print_function
print(123)
123

2.2 文本字符串

字符串字面量的类型为文本(Python2 中的 unicode,Python3 中的 str),而不是字节(Python2中的 str,Python3 中的 bytes)。

1
2
3
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
print('试试看')

2.3 引入模块时,优先绝对路径

使用绝对路径导入模块时,Python 会在 sys.path 里寻找模块。

在Python 2.4 或之前, Python 会先查找当前目录下有没有模块, 若找到了,则引入该模块。

1
from __future__ import absolute_import

2.4 使用浮点除法

Python3 中 int 除以 int 得float,而 Python2 使用的是整除。

1
2
3
from __future__ import division
print 3/2
1.5

3. six 模块

six 是一个专门用来兼容 Python 2 和 Python 3 的库。six 重新定义了在 Python2 和 Python3 中有差异的函数,six 会根据 Python 解释器的版本调用合适的处理函数。

3.1 常量定义兼容

  • six.PY2/ six.PY3 ,布尔值。检查编译器版本是否为 Python2 或 Python3
  • six.class_types,类类型。在 Python2 中包含旧类和新类。在 Python3 中只是新类
  • six.integer_types,整数类型。在 Python2 中是 long 或 int,在 Python3 中是 int
  • six.string_types,文本数据的类型。Python2 中是 basestring() , Python3 中是 str
  • six.text_type,用于表示(Unicode)文本数据的类型。Python2 中是 unicode() ,Python3 中是 str(Pyhon3 对文本数据进行了整合,默认为 Unicode 文本数据)
  • six.binary_type,二进制数据的类型。Python2中是 str,Python3 中是 bytes
  • six.MAXSIZE, list 或 dict 等容器的最大尺寸。这相当于 Python 2.6 及更高版本(包括3.x)的 sys.maxsize。在 Python3 中没有直接的等价物, 因为它的整数类型的长度仅受限于内存大小

使用示例:

1
2
3
4
5
6
7
8
9
import six

def dispatch_types(value):
    if isinstance(value, six.integer_types):
        handle_integer(value)
    elif isinstance(value, six.class_types):
        handle_class(value)
    elif isinstance(value, six.string_types):
        handle_string(value)

3.2 模块位置兼容

Python3 重新组织了很多模块的位置,例如 Python2 的 HTMLParser,在 Python3 中是 html.parser。

可以使用 six 导入,兼容模块导入:

1
from six.moves import html_parser

在大多数情况下,six.moves 别名是 Python3 中模块的名称。

这里 ,你可以看到一个 Supported renames 名单。

其他
其他的内容可以在官方的文档找到,基本上就是通过six来调用,而不是自己对Python判断。包括:

3.3 其他

除了上面的兼容性操作,six 还提供了:

  • 二进制和文本数据的兼容
  • uniittest assert的兼容
  • urllib 库改动的兼容
  • 高级的自定义move

4. 参考


微信公众号
作者
微信公众号