【问题标题】:Is there a way to know by which Python version the .pyc file was compiled?有没有办法知道 .pyc 文件是由哪个 Python 版本编译的?
【发布时间】:2011-12-10 02:07:47
【问题描述】:

有没有办法知道.pyc文件是用哪个Python版本编译的?

【问题讨论】:

    标签: python compilation


    【解决方案1】:

    .pyc 文件的前两个字节是表示字节码版本的幻数。单词以little-endian格式存储,已知值为:

    # Python/import.c - merged by aix from Python 2.7.2 and Python 3.2.2
    # EDIT: added little endian hex values for comparison first two bytes of Igor Popov's method -jimbob
    
           Python 1.5:   20121  0x994e
           Python 1.5.1: 20121  0x994e
           Python 1.5.2: 20121  0x994e
           Python 1.6:   50428  0x4cc4
           Python 2.0:   50823  0x87c6
           Python 2.0.1: 50823  0x87c6
           Python 2.1:   60202  0x2aeb
           Python 2.1.1: 60202  0x2aeb
           Python 2.1.2: 60202  0x2aeb
           Python 2.2:   60717  0x2ded
           Python 2.3a0: 62011  0x3bf2
           Python 2.3a0: 62021  0x45f2
           Python 2.3a0: 62011  0x3bf2 (!)
           Python 2.4a0: 62041  0x59f2
           Python 2.4a3: 62051  0x63f2
           Python 2.4b1: 62061  0x6df2
           Python 2.5a0: 62071  0x77f2
           Python 2.5a0: 62081  0x81f2 (ast-branch)
           Python 2.5a0: 62091  0x8bf2 (with)
           Python 2.5a0: 62092  0x8cf2 (changed WITH_CLEANUP opcode)
           Python 2.5b3: 62101  0x95f2 (fix wrong code: for x, in ...)
           Python 2.5b3: 62111  0x9ff2 (fix wrong code: x += yield)
           Python 2.5c1: 62121  0xa9f2 (fix wrong lnotab with for loops and
                                storing constants that should have been removed)
           Python 2.5c2: 62131  0xb3f2 (fix wrong code: for x, in ... in listcomp/genexp)
           Python 2.6a0: 62151  0xc7f2 (peephole optimizations and STORE_MAP opcode)
           Python 2.6a1: 62161  0xd1f2 (WITH_CLEANUP optimization)
           Python 2.7a0: 62171  0xdbf2 (optimize list comprehensions/change LIST_APPEND)
           Python 2.7a0: 62181  0xe5f2 (optimize conditional branches:
                    introduce POP_JUMP_IF_FALSE and POP_JUMP_IF_TRUE)
           Python 2.7a0  62191  0xeff2 (introduce SETUP_WITH)
           Python 2.7a0  62201  0xf9f2 (introduce BUILD_SET)
           Python 2.7a0  62211  0x03f3 (introduce MAP_ADD and SET_ADD)
    
           Python 3000:   3000  0xb80b
                          3010  0xc20b (removed UNARY_CONVERT)
                          3020  0xcc0b (added BUILD_SET)
                          3030  0xd60b (added keyword-only parameters)
                          3040  0xe00b (added signature annotations)
                          3050  0xea0b (print becomes a function)
                          3060  0xf40b (PEP 3115 metaclass syntax)
                          3061  0xf50b (string literals become unicode)
                          3071  0xff0b (PEP 3109 raise changes)
                          3081  0x090c (PEP 3137 make __file__ and __name__ unicode)
                          3091  0x130c (kill str8 interning)
                          3101  0x1d0c (merge from 2.6a0, see 62151)
                          3103  0x1f0c (__file__ points to source file)
           Python 3.0a4:  3111  0x270c (WITH_CLEANUP optimization).
           Python 3.0a5:  3131  0x3b0c (lexical exception stacking, including POP_EXCEPT)
           Python 3.1a0:  3141  0x450c (optimize list, set and dict comprehensions:
                   change LIST_APPEND and SET_ADD, add MAP_ADD)
           Python 3.1a0:  3151  0x4f0c (optimize conditional branches:
               introduce POP_JUMP_IF_FALSE and POP_JUMP_IF_TRUE)
           Python 3.2a0:  3160  0x580c (add SETUP_WITH)
                         tag: cpython-32
           Python 3.2a1:  3170  0x620c (add DUP_TOP_TWO, remove DUP_TOPX and ROT_FOUR)
                         tag: cpython-32
           Python 3.2a2   3180  0x6c0c (add DELETE_DEREF)
    

    【讨论】:

    • @aix 也是一个不错的答案,但通常像@Popov 给出的那样,编程解决方案很容易理解。真的谢谢。
    • @AamirAdnan:但是如果你碰巧没有创建 .pyc 文件的 python 版本呢?
    • 没问题。您的回答很棒,现在它使用with open('test51.pyc') as f: magic=f.read(2); magic.encode('hex') 进行了简单的表格查找。
    • @Jefromi 哦,是的,这很重要,在这种情况下,@Popov 解决方案Fails 然后。因为它永远不会与 python version hex code 匹配到 .pyc 文件的 hex code of first two bytes。而且我永远不会知道版本是什么。最终我必须去@aix 解决方案进行 Python 版本查找。我说的对吗?:)
    【解决方案2】:

    您可以通过以下方式获取 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'
    

    通过比较魔术数字,您将知道生成 pyc 文件的 python 版本。

    【讨论】:

    • 真的是寻找 Python 版本的好方法 :)
    • 比较Python source 中以整数形式写入的魔法字与imp.get_magic() 字节(Python 3):int.from_bytes(imp.get_magic()[:2], 'little')(忽略b'\r\n') 例如,imp.get_magic() == b'3\r\r\n' 对应于3379 (Python 3.6)。
    【解决方案3】:

    看看我在 Python 中的script,它检测并返回编译文件(*.pyc 或 *.pyo)的 Python 版本。

    它检测从 Python 1.5 到最后一个 Python 3 版本的 Python 版本。

    【讨论】:

    • 我在使用其他答案时找不到我的幻数,感谢脚本!
    【解决方案4】:

    或者,如果您有 GNU/Linux 系统,您可以在终端中使用命令“file”:

    $ file code.pyc
    > code.pyc: python 3.5.2 byte-compiled
    

    【讨论】:

    【解决方案5】:

    官方 Python github 存储库似乎不再将列表保留在 import.c 中。

    在搜索比我在其他地方找不到的更新列表时,我遇到了 Google 提供的截至 2017 年 5 月的最新列表。

    https://github.com/google/pytype/blob/master/pytype/pyc/magic.py

    【讨论】:

    【解决方案6】:

    添加到@Igor Popov 的答案。

    从其他版本的python检查编译脚本的版本。

    # written in python3
    
    filename = "outfit.cpython-39.pyc"
    
    with open(filename,'rb') as f:
        magic = f.read(4)
    
    print(int.from_bytes(magic[:2], 'little'))
    

    您可以在此处查找输出编号: https://github.com/google/pytype/blob/master/pytype/pyc/magic.py

    【讨论】:

      猜你喜欢
      • 2010-11-10
      • 1970-01-01
      • 1970-01-01
      • 2013-07-28
      • 1970-01-01
      • 2023-02-13
      • 2021-08-18
      • 2023-03-03
      • 1970-01-01
      相关资源
      最近更新 更多