杰瑞科技汇

Python2与Python3模块如何兼容?

Python 3 对其标准库中的许多模块进行了重命名、重组、功能增强和现代化改造,以使其命名更清晰、功能更强大,并更好地适应 Python 3 的设计理念(如更严格的 Unicode 支持)。

Python2与Python3模块如何兼容?-图1
(图片来源网络,侵删)

以下是几个最关键、最常见的模块差异点:


print 语句 vs print() 函数

这是最著名、也是影响最广的变化。

  • Python 2: print 是一个语句

    # Python 2
    print "Hello, World!"  # 直接打印
    print "Hello", "World"  # 打印两个值,并用空格分隔
    print >> sys.stderr, "Error!" # 打印到标准错误流
  • Python 3: print 是一个内置函数

    Python2与Python3模块如何兼容?-图2
    (图片来源网络,侵删)
    # Python 3
    print("Hello, World!")  # 作为函数调用,必须有括号
    print("Hello", "World")  # 函数参数,默认用空格分隔
    print("Hello", "World", sep="|") # 可以指定分隔符
    print("Hello", "World", end="\n") # 可以指定结尾符
    # print >> sys.stderr, "Error!" # 这种语法已不再支持
    import sys
    print("Error!", file=sys.stderr) # 使用 file 参数指定输出流

迁移影响: 所有使用 print 的代码都需要修改,可以使用 2to3 工具自动完成大部分修改。


核心库模块的重命名与合并

Python 3 将一些功能相似或重复的模块进行了合并,并统一了命名风格(通常是 foofoo_py 风格)。

Python 2 模块 Python 3 模块 说明
ConfigParser configparser 统一为小写。
Queue queue 统一为小写。PriorityQueue 等也在此模块中。
SocketServer socketserver 统一为小写。
repr repr 内置函数,模块名未变,但功能行为有细微差别(见下文)。
commands 已移除 功能被 subprocess 模块完全取代。这是最重要的迁移点之一。
__builtin__ builtins 内置函数和异常所在的模块名改变。import __builtin__ -> import builtins
urllib2, urlparse, robotparser urllib, urllib.parse, urllib.robotparser 在 Python 3 中被重组到 urllib 包下。
BaseHTTPServer, SimpleHTTPServer http.server 合并到 http.server 模块。
SocketSSL ssl 功能整合到 ssl 模块。
cPickle pickle cPickle 模块被移除。pickle 模块现在默认使用 C 语言实现,性能和 cPickle 一样好。

迁移影响:

  • 直接导入错误: 如果你直接导入 import ConfigParser,在 Python 3 中会直接报错,需要修改为 import configparser
  • 功能迁移: 对于 commands 模块,需要将 commands.getstatusoutput() 等调用重写为使用 subprocess 模块的等效代码。

unicodestr 的根本性变化

这是理解 Python 2 和 Python 3 模块行为差异的基础。

  • Python 2:

    • str: 字节串。'hello' 是一个字节串。
    • unicode: Unicode 字符串。u'hello' 是一个 Unicode 字符串。
    • 混合使用 strunicode 经常会导致 UnicodeDecodeErrorUnicodeEncodeError,处理文件 I/O、网络请求时,需要手动进行编解码。
  • Python 3:

    • str: Unicode 字符串,这是文本的默认类型。'hello' 是一个 Unicode 字符串。
    • bytes: 字节串,这是二进制数据的类型。b'hello' 是一个字节串。
    • 这种设计使得文本和二进制数据分离得非常清晰,减少了编码错误。

模块层面的影响: 所有处理 I/O、网络、文件、数据的模块都必须适应这个变化。

  • 文件操作:

    • Python 2: open('file.txt', 'r') 默认以系统编码读取文件,可能出错,推荐使用 io.open() 并指定编码。
    • Python 3: open('file.txt', 'r') 默认以文本模式打开,返回 str 对象。open('file.txt', 'rb') 以二进制模式打开,返回 bytes 对象,编码问题被更好地封装。
  • json 模块:

    • json.loads() 接受一个 str (包含 JSON 文本) 并返回一个 Python 对象。
    • json.dumps() 接受一个 Python 对象并返回一个 str
    • 如果你有一个 bytes 对象,需要先解码成 strjson.loads(my_bytes.decode('utf-8'))
  • re (正则表达式) 模块:

    • Python 2: re 模块函数可以接受 strunicode 字符串,如果模式是 str 而字符串是 unicode,它会尝试隐式解码,这可能导致不可预测的行为。
    • Python 3: re 模块被严格化,模式字符串和被处理的字符串必须是同一种类型,要么都是 str,要么都是 bytes,这迫使开发者更清晰地思考是在处理文本还是二进制数据。

第三方库的兼容性

这是实际开发中遇到最多的问题。

  • 现状:

    1. 仅支持 Python 2: 许多老旧项目或特定工具库(如一些科学计算库的旧版本)已停止更新,不再维护。
    2. 仅支持 Python 3: 大多数现代库和框架(如 Django, Flask, Pandas, NumPy, Requests 等)都已转向 Python 3。
    3. 支持 Python 2 和 Python 3 (兼容层): 许多库通过 sixfuturepast 等工具来同时支持两个版本。
  • 如何处理:

    • 首选方案: 尽可能使用只支持 Python 3 的库,这是未来的趋势。
    • 遗留系统: 如果必须使用一个只支持 Python 2 的库,那么你的项目暂时无法迁移到 Python 3,你需要考虑:
      • 是否有替代的、支持 Python 3 的库?
      • 是否可以自己动手修改或 Fork 这个库(如果许可证允许)?
      • 在容器或虚拟环境中保留一个 Python 2 的运行环境来运行这个特定库(不推荐,会增加系统复杂性)。

迁移策略与工具

面对这些模块差异,如何有效地进行迁移?

  1. 使用自动化工具:

    • 2to3: Python 自带的翻译工具,它可以自动修复大部分语法和模块导入问题(如 print 语句、ConfigParser 等),你可以对单个文件或整个项目运行它。
    • modernize: 一个更智能的第三方工具,它基于 lib2to3,但会建议使用更现代的 Python 3 风格的代码,而不仅仅是修复语法错误。
  2. 使用兼容性库:

    • six: 一个轻量级的库,用于编写在 Python 2 和 Python 3 上都能运行的代码,它提供了一些辅助函数和类来弥合两个版本之间的差异。

      # 使用 six 来兼容 print
      from six import print_
      print_("Hello, World!")
      # 使用 six 来兼容字符串类型
      import six
      text_type = six.text_type  # 在 Python 2 中是 unicode, Python 3 中是 str
      binary_type = six.binary_type # 在 Python 2 中是 str, Python 3 中是 bytes
    • future: 一个更全面的库,它允许你在 Python 2 代码中导入 Python 3 的行为(from __future__ import print_function)。

  3. 使用条件导入: 对于一些简单的模块名变化,可以使用 try...except 来处理。

    try:
        # 尝试 Python 3 的导入方式
        import configparser
    except ImportError:
        # 如果失败,说明是 Python 2,使用旧的名字
        import ConfigParser as configparser

    这种方法对于少量模块有效,但如果差异很大,代码会变得很臃肿。

方面 Python 2 Python 3 迁移要点
print 语句 函数 所有 print 语句需加括号。
模块命名 混合风格(如 ConfigParser 统一小写风格(如 configparser 导入语句需修改。
模块合并 功能分散(如 urllib2 功能重组(如 urllib.request 导入路径和API可能改变。
字符串 str (字节), unicode (文本) str (文本), bytes (字节) 文本和二进制数据分离,I/O处理方式改变。
第三方库 大量库仅支持 Python 2 主流库已转向 Python 3 评估依赖库的兼容性是迁移的关键。

迁移过程本质上就是系统性地识别并修复所有因这些核心差异而导致的代码不兼容问题,从自动化工具开始,然后针对遗留问题手动修复,是最高效的迁移路径。

分享:
扫描分享到社交APP
上一篇
下一篇