Python 2.5的.pyc文件能否和Python 2.6的.pyc文件兼容?

18
不久之前,我需要将一些服务器从Python 2.4升级到Python 2.5。我发现在Python 2.4下创建的.pyc文件,当尝试在Python 2.5下运行时会崩溃。
如果我从2.5升级到2.6,这种情况会再次发生吗?
编辑:以下是更详细的信息
我有一个文件服务器,其中包含Python代码。Ubuntu和Windows服务器都可以访问该服务器来运行Python代码。当它们运行代码时,会在文件服务器上生成.pyc文件。
我发现当我将其中一台服务器机器从Python 2.4升级到2.5时,我遇到了.pyc文件的问题。我现在不确定是一台运行2.5的机器尝试运行2.4字节码,还是一台运行2.4的机器尝试运行2.5字节码,但是如果我删除字节码,一切都很顺利,直到下一个字节码冲突。
我将所有机器升级到2.5,问题就解决了。
5个回答

17
一般来说,.pyc文件是特定于某个Python版本的(尽管在不同机器架构间是可移植的,只要它们运行相同版本)。这些文件在头部携带有相关Python版本的信息。因此,如果您将相应的.py文件和.pyc文件放在一起,那么每次使用不同的Python版本导入这些模块时,.pyc文件都将被重新生成。我从未听说过“尝试运行”错误版本的.pyc文件。有哪些架构涉及其中?是否存在应该存在的.py文件?

编辑:由于原帖澄清了崩溃是在同时运行Python 2.4和Python 2.5程序的相同.py文件上发生的(来自两个不同的服务器,共享网络驱动器),因此崩溃的解释变得容易。 .py文件一直在被重新编译-当2.5最近运行它们时,由2.4 Python重新编译,反之亦然-因此.pyc文件一直在疯狂地被重写。在网络驱动器上正确地锁定文件(特别是跨不同操作系统)非常难以实现。所以以下情况可能已经发生(角色可以交换):2.4服务器刚刚确定.pyc文件对其有用,并开始读取它;在完成阅读之前,2.5服务器(先前确定需要重新编译模块)覆盖了它;因此,2.4服务器最终获得了一个内存缓冲区,其中包含(假设)来自2.4版本的前4K字节和来自2.5版本的下一个4K字节。当它然后使用那个混乱的缓冲区时,毫不奇怪……崩溃

如果你在同一台服务器上使用两个或更多版本的Python(甚至没有网络驱动器的情况下),并且不断尝试运行单个.py文件集,则可能会遇到真正的问题。"正确"的解决方案是使用virtualenv之类的工具。我们在工作中采用了一个(简单但有点脏的)技巧,即对每个Python版本进行修补,以生成和使用其编译后的字节码文件的不同扩展名:.pyc(或.pyo)适用于Python 1.5.2(这是我们开始将此补丁应用于更新版本的最稳定的“系统”版本),.pyc-2.0适用于2.0,.pyc-2.2适用于2.2等等(当然也可以使用相应的.pyo-X.Y)。我听说这个问题很快就要消失了(感谢Thomas!),但它确实在许多年里半途而废地解决了这个棘手的问题。
一个更简单的解决方案是保留一个Python版本,如果您的系统允许的话;如果您的系统存在任何使保留单个Python版本变得不可行的复杂性(如我们的系统所示),那么现在我强烈推荐使用virtualenv,这是我已经提到的。
使用Python 3.2中的PEP 3147,不同版本的pyc文件会通过文件名自动区分。这应该解决了不同Python版本互相覆盖pyc文件的大部分问题。

有时候人们会尝试分发没有 .py 文件的 .pyc 文件作为一种知识产权保护形式。 - John La Rooy
@gnibbler:是的,但那很愚蠢,因为.pyc文件非常易读,并且可以轻松地反编译成非常接近原始代码的表示形式。 - nosklo
谢谢您的回复。文件锁定错误让我感到惊讶。这意味着我不能一次在一个服务器上从Python 2.5升级到2.6,而必须在将它们全部升级之前关闭它们并升级它们,然后再重新启动它们。 - pwdyson
@pwdyson 对的,在Python版本中,如果尝试同时导入“相同”的.py模块,则无法插入偏差--除非您通过virtualenv将它们彼此隔离,复制包含.py(和.pyc)文件的目录等。 - Alex Martelli

6
如果您有源代码,那么它会为您重新编译。一般情况下,您没有问题。
但是,如果使用不同版本的Python的用户从中央安装目录运行,则可能对您不利。
如果只有pyc文件,则也可能不好。我为您进行了快速测试。我创建了两个.pyc文件。一个在2.5中,一个在2.6中。2.5无法在2.6中运行,2.6无法在2.5中运行。两者都会抛出“ImportError: Bad magic number in ..”错误,这是有道理的,因为魔术数字已从2.5更改为2.6。
如果您想提前确定这一点,可以按以下方式获取Python的魔术数字:
$ python -V
Python 2.6.2
# python
>>> import imp
>>> imp.get_magic().encode('hex')
'd1f20d0a'

要获取pyc文件的魔数,您可以执行以下操作:
>>> f = open('test25.pyc')
>>> magic = f.read(4)
>>> magic.encode('hex')
'b3f20d0a'
>>> f = open('test26.pyc')
>>> magic = f.read(4)
>>> magic.encode('hex')
'd1f20d0a'

你能详细说明一下吗?因为这种情况可能会对你造成影响:“但是,如果使用不同版本的Python的用户从一个中央代码库运行,那么这可能会对你不利。” - pwdyson
当然。如果我们首先假设一个使用Python 2.5的人已经使用了存储库,那么pyc将是2.5 pyc。当使用Python 2.6的人使用存储库时,他们将把导入文件的pyc转换为2.6 pyc。他们还会因此而减慢速度。如果不同版本的人同时尝试使用pyc,您也可能会遇到问题。只是为了明确一下,我所说的这个存储库是人们运行代码的地方,而不是检查代码的进出。 - Paul Hildebrandt
我编辑了我的原始答案,并将“repository”一词更改为“安装目录”,以确保表述清晰。 - Paul Hildebrandt
感谢指出当不同版本的Python尝试同时使用它们时会出现问题。我想我可以接受这一点。我记得在其他时候也出现过问题。不幸的是,这些都是从记忆中得来的,因为我认为我已经找到了错误的来源(不同版本)并修复了它们(使它们都成为相同的版本)。根据大家所说,让我将一个服务器升级到2.6并将其余部分保留为2.5应该是安全的。这样可以让我逐步升级并检查各个方面,而不必一次性全部完成。 - pwdyson
2
@pwydson,不要这样做——它是 _不安全的_;请阅读我编辑后的答案,了解为什么混合使用版本会导致崩溃。使用virtualenv(或以其他方式隔离2.5和2.6 pyc文件写入的目录),或者坚持使用单个版本,而不是多个版本。 - Alex Martelli
我同意Alex的观点,这样做并不安全。当我们遇到这个问题时,我们创建了两个安装目录,一个用于每个Python版本。这样做不仅可以避免减速,更重要的是可以避免两个版本同时访问相同文件时的崩溃。 - Paul Hildebrandt

2
创建文件的Python版本存储在.pyc文件本身中。 通常意味着.pyc将被具有正确Python版本的文件所替换。
以下是可能不会发生的一些原因: - 权限问题 - .py文件不可用
如果存在权限问题,Python将仅使用.py并忽略.pyc(以性能为代价)。
我认为在次要版本之间是可以的,例如Python2.6.2 .pyc应该与Python2.6.4一起使用。
这是来自/usr/share/file/magic的摘录。
# python:  file(1) magic for python
0   string      """ a python script text executable
0   belong      0x994e0d0a  python 1.5/1.6 byte-compiled
0   belong      0x87c60d0a  python 2.0 byte-compiled
0   belong      0x2aeb0d0a  python 2.1 byte-compiled
0   belong      0x2ded0d0a  python 2.2 byte-compiled
0   belong      0x3bf20d0a  python 2.3 byte-compiled
0   belong      0x6df20d0a  python 2.4 byte-compiled
0   belong      0xb3f20d0a  python 2.5 byte-compiled
0   belong      0xd1f20d0a  python 2.6 byte-compiled

因此,您可以看到正确的Python版本由.pyc文件的前4个字节指示。


这意味着你的 .py 文件不可用。 - nosklo

1

这实际上是一个不同的PEP,用于解决非常相似但不同的问题;具体来说,它是用于区分为不兼容ABIs编译的扩展模块。关于pyc文件的PEP是[PEP 3147](https://www.python.org/dev/peps/pep-3147/)。 - user2357112

0

你肯定需要重新编译字节码文件才能使用它们。Python 的每个主要版本都更改了字节码魔数(*)。

然而,非版本匹配的字节码文件不应该导致 Python 崩溃。通常它只会忽略任何没有正确版本号的字节码,所以不应该有错误,只是第一次重新编译时会变慢(或者如果运行脚本的用户没有写入权限来更新字节码,则每次都会变慢)。

(*: 而且在开发阶段经常更改,早期版本中有时也会在小版本之间更改。请参见 import.c 以获取魔数及其对应的 Python 版本的完整列表。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接