【问题标题】:How to write Python 2.x as much compatible with Python 3.x as possible?如何编写尽可能与 Python 3.x 兼容的 Python 2.x?
【发布时间】:2011-12-14 02:18:20
【问题描述】:

有很多方法可以在 Python 2.x 中包含 Python 3.x 功能,因此 Python 2.x 脚本的代码将来可以轻松转换为 Python 3.x。这些示例之一是将 print 语句替换为 print() 函数:

>>> from __future__ import print_function

是否有任何列表或资源可以提供一些关于如何使 Python 2.x 代码尽可能接近 Python 3.x 的想法?

您能否举例说明其他有用的导入或定义,它们可以使 Python 2.x 的外观和行为更像 Python 3.x?

假设我们拥有最新的 Python 2.x(我相信目前是 2.7.2)。

【问题讨论】:

  • 回答this 问题可能会有所帮助。
  • @BoraCaglayan:是的,标题与这个问题的几乎完全相同:) 但不幸的是,该链接是关于 Django 应用程序的(意味着存在现有的代码库,你不能破坏应用程序)。我的问题是关于新创建的应用程序,您无需担心,例如。有很多代码使用print 语句而不是函数。我相信这消除了很多问题。
  • @Tadeck:不,打印功能是一个非常微不足道的问题。大多数问题都是一样的,而且要困难得多。使用 Django 并不能真正避免任何兼容性问题。
  • @LennartRegebro:我想说的是,这个问题是关于在 Python 2.x 中以尽可能接近 Python 3.x 的方式创建新应用程序。我认为使用 Django 会产生一些问题,因为您已经有一个与 Python 3.x 编码方式不兼容的代码,因此您的选择是有限的。如果我错了,请纠正我。
  • 将自己限制在 2.7(甚至包括 2.6)是好的。 – 也将自己限制在 3.3 或更高版本! 3.3 重新引入u'' 语法!

标签: python python-3.x compatibility python-2.x


【解决方案1】:

我正在对运行在 CPython 2.[567]、CPython 3.[0123] 上的大约 5000 行重复数据删除备份程序 (http://stromberg.dnsalias.org/~strombrg/backshift/) 进行最后润色(3.3 仍然是 alpha 0)、Pypy 1.7 和 Jython 主干。我也尝试过 IronPython,但它是完全不同的东西——它没有标准库,所以没有 backshift 的爱。哦,它可以使用 Cython 作为其最内层的循环,或者 psyco - 但 pypy 比这两者都快,尤其是在 32 位系统上。

无论如何,我发现要编写在 2.x 和 3.x 上同样运行良好的代码,我所需要做的就是:

1) print(variable) 在 2.x 和 3.x 上的工作方式相同。 print(variable1, variable2) 没有。在 2.x 中,print(variable) 表示“评估这个带括号的表达式,并使用 print 语句打印单个结果”。在 3.x 中, print(variable) 表示“在这个单一结果上调用 print 函数。所以 print('abc %d %d' % (1, 2)) 在两者中都可以正常工作,因为它是一个单值结果,并且都使用 % 运算符进行字符串格式化。

2) 避免使用八进制常量。不要写 0755,而是写 (7*64 + 5*8 + 5)。

3) 要在其中一个中执行二进制 I/O,我使用了我的 bufsock 模块。 http://stromberg.dnsalias.org/~strombrg/bufsock.html 我会 os.open 一个文件,并用 bufsock 包装它(或使用模块中的 rawio 类)。在 2.x 上,这将返回编码为 8 位字符串的字节字符串。在 3.x 上,这将返回一个字节对象,它的行为很像一个小整数列表。然后我只是传递一个或另一个,根据需要使用“isinstance(foo,str)”进行测试以区分两者。我这样做是因为对于备份程序来说,字节就是字节——我不想乱用编码来破坏可靠地保存数据,而且并不是所有的编码都能很好地往返。

4) 做异常时,避免使用“as”关键字。相反,使用 EG:

  try:
     self.update_timestamp()
  except (OSError, IOError):
     dummy, utime_extra, dummy = sys.exc_info()
     if utime_extra.errno == errno.ENOENT:

5) 一堆模块在从 2.x 到 3.x 的过渡中被重命名。因此,请尝试将其中一个导入到原本为空的模块中,例如:

try:
   from anydbm import *
except ImportError:
   from dbm import *

...这将单独出现在一个模块中,名称为 EG adbm.py。然后,每当我需要键值存储时,我都会导入 adbm 而不是直接导入 2.x 或 3.x 所需的两种不同的东西。然后我会 pylint 除了那个粗短的模块 adbm.py 之外的所有东西 - 以及 pylint 不喜欢的东西。我们的想法是对所有可能的东西进行 pylint,除了“一切都需要 pylint”规则之外,只在一个小模块中单独使用,每个模块一个例外。

6) 设置在 2.x 和 3.x 上运行的自动单元测试和系统测试,然后在至少一个 2.x 解释器和至少一个 3.x 上频繁测试,这很有帮助口译员。我也经常对我的代码运行 pylint,尽管只是一个检查 2.5.x 合规性的 pylint - 我在 pylint 获得 3.x 支持之前启动了该项目。

7) 我设置了一个小的“python2x3”模块,其中包含一些常量和可调用函数以使生活更轻松:http://stromberg.dnsalias.org/svn/python2x3/trunk/python2x3.py

8) b'' 字面量在 2.5 中不起作用,尽管它们在 2.[67] 中起作用。我没有尝试预处理或其他什么,而是设置了一个 constants_mod.py,其中包含很多通常在 3.x 中是 b'' 文字的东西,并将它们从简单的字符串转换为 2 的任何“字节”类型.x 或 3.x。所以它们在模块导入时被转换一次,而不是在运行时一遍又一遍。如果您的目标是 2.[67]及以上,也许有更好的方法,但是当我开始 Pypy 项目时,它只与 2.5 兼容,而 Jython 仍然如此。

9) 在 2.x 中,长整数有一个 L 后缀。在 3.x 中,所有整数都是长整数。所以我只是尽可能避免使用长整数常量; 2.x 会根据需要将整数提升为 long,所以这对于大多数事情来说似乎都很好。

10) 有一堆 Python 解释器可以用来进行测试,这很有帮助。我建了 2.[567]和 3. [0123]并将它们存放在 /usr/local/cpython-x.y/ 以便于测试。我还将一些 Pypy 和 Jython 放在 /usr/local 中,再次方便测试。拥有一个自动化 CPython 构建的脚本非常有价值。

我相信这些都是我在一个不平凡的项目中获得高度可移植的 python 代码库所需的所有扭曲。我上面写的列表中的一大遗漏是,我没有尝试使用 unicode 对象——其他人可能更有资格评论。

HTH

【讨论】:

  • 嘿,你知道 sys.exc_info() 技巧会让 pypy 哭吗?
  • 不,我不是。谢谢你提到这一点。在 Pypy 中修复它需要什么?
  • 真的吗?这似乎有效: >>>> try: .... dummy = 1/0 .... except: .... dummy, foo, dummy = sys.exc_info() .... print foo ... . 整数除以零 >>>>
  • 它有效,但 sys.exc_info() 使整个帧堆栈出现。这对于调试器等(如它的设计)来说很好,但对于只有一个异常对象来说,这是一个巨大的矫枉过正。 JIT 将中止跟踪和编译并退出汇编程序。
  • 对于那些关注sys.exc_info()的人:如果你只支持Python 2.6+并且不支持旧版本,实际上没有必要避免使用except ... as ...。
【解决方案2】:

您应该查看Porting Python Code to 3.0。虽然它的目的是移植,但它基本上回答了相同的问题;你不会一路走下去。

【讨论】:

  • 谢谢。我对您提供的链接中的第二个选项特别感兴趣:“使代码在 Python 2 和 Python 3 中都未经修改地运行”。这个选项最接近我想要实现的目标。
【解决方案3】:

在“Porting to Python 3”中有a whole chapter。也不要错过附录,其中列出了语言差异以及支持两种语言的解决方法。

您可能想使用six library,尽管没有它也有可能。

【讨论】:

    【解决方案4】:

    Porting Python 2 code to Python 3 是官方文档的一部分,但仍未直接回答您的问题,可能会有所帮助。

    【讨论】:

      【解决方案5】:

      我的 Python 2.7 脚本模板顶部有这个:

      from __future__ import division, print_function
      from future_builtins import ascii, filter, hex, map, oct, zip
      

      【讨论】:

      • 这远远不够实际使用。而 future_builtins 在 Python 3 中不存在,所以这段代码实际上不会在 Python 3 上运行。
      • 将 furure_builtins 模块保留在 Python 3 标准库中,以便更容易地从通用代码库支持 Python2 和 Python3 版本不是更实用吗?
      【解决方案6】:

      因为它还没有被提及:我发现这个cheat sheet 对于这个确切的目的非常有用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-09-15
        • 1970-01-01
        • 1970-01-01
        • 2023-03-16
        • 2015-12-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多