展开菜单
首页 精品内容 本月促销 装机必备 Windows PDF教程 macOS软件 IOS软件 Android AI 专题
全部分类

当前位置:

首页 > 编程开发 > python虚拟机怎么使用

python虚拟机怎么使用

python字节码设计一条python字节码主要有两部分组成,一部分是操作码,一部分是这个操作码的参数,在cpython当中只有部分字节码有参数,如果对应的字节码没有参数,那么oparg的值就等于0,在cpython当中opcode

本文目录

    python 字节码设计

    一条 python 字节码主要有两部分组成,一部分是操作码,一部分是这个操作码的参数,在 cpython 当中只有部分字节码有参数,如果对应的字节码没有参数,那么 oparg 的值就等于 0 ,在 cpython 当中 opcode < 90 的指令是没有参数的。

    python虚拟机怎么使用

    opcode 和 oparg 各占一个字节,cpython 虚拟机使用小端方式保存字节码。

    我们使用下面的代码片段先了解一下字节码的设计:

    import dis
    
    
    def add(a, b):
        return a + b
    
    
    if __name__ == '__main__':
        print(add.__code__.co_code)
        print("bytecode: ", list(bytearray(add.__code__.co_code)))
        dis.dis(add)

    上面的代码在 python3.9 的输出如下所示:

    b'|\x00|\x01\x17\x00S\x00'
    bytecode:  [124, 0, 124, 1, 23, 0, 83, 0]
      5           0 LOAD_FAST                0 (a)
                  2 LOAD_FAST                1 (b)
                  4 BINARY_ADD
                  6 RETURN_VALUE

    首先 需要了解的是 add.__code__.co_code 是函数 add 的字节码,是一个字节序列,list(bytearray(add.__code__.co_code)) 是将和这个序列一个字节一个字节进行分开,并且将其变成 10 进制形式。根据前面我们谈到的每一条指令——字节码占用 2 个字节,因此上面的字节码有四条指令:
    python虚拟机怎么使用

    操作码和对应的操作指令在文末有详细的对应表。在上面的代码当中主要使用到了三个字节码指令分别是 124,23 和 83 ,他们对应的操作指令分别为 LOAD_FAST,BINARY_ADD,RETURN_VALUE。他们的含义如下:

    LOAD_FAST:将 varnames[var_num] 压入栈顶。 BINARY_ADD:从栈中弹出两个对象并且将它们相加的结果压入栈顶。 RETURN_VALUE:弹出栈顶的元素,将其作为函数的返回值。

    首先我们需要知道的是 BINARY_ADD 和 RETURN_VALUE,这两个操作指令是没有参数的,因此在这两个操作码之后的参数都是 0 。

    但是 LOAD_FAST 是有参数的,在上面我们已经知道 LOAD_FAST 是将 co-varnames[var_num] 压入栈,var_num 就是指令 LOAD_FAST 的参数。在上面的代码当中一共有两条 LOAD_FAST 指令,分别是将 a 和 b 压入到栈中,他们在 varnames 当中的下标分别是 0 和 1,因此他们的操作数就是 0 和 1 。

    字节码扩展参数

    在上面我们谈到的 python 字节码操作数和操作码各占一个字节,但是如果 varnames 或者常量表的数据的个数大于 1 个字节的表示范围的话那么改如何处理呢?

    为了解决这个问题,cpython 为字节码设计的扩展参数,比如说我们要加载常量表当中的下标为 66113 的对象,那么对应的字节码如下:

    [144, 1, 144, 2, 100, 65]

    其中 144 表示 EXTENDED_ARG,他本质上不是一个 python 虚拟机需要执行的字节码,这个字段设计出来主要是为了用与计算扩展参数的。

    100 对应的操作指令是 LOAD_CONST ,其操作码是 65,但是上面的指令并不会加载常量表当中下标为 65 对象,而是会加载下标为 66113 的对象,原因就是因为 EXTENDED_ARG 。

    现在来模拟一下上面的分析过程:

    先读取一条字节码指令,操作码等于 144 ,说明是扩展参数,那么此时的参数 arg 就等于 (1 x (1 << 8)) = 256 。 读取第二条字节码指令,操作码等于 144 ,说明是扩展参数,因为前面 arg 已经存在切不等于 0 了,那么此时 arg 的计算方式已经发生了改变,arg = arg << 8 + 2 << 8 ,也就是说原来的 arg 乘以 256 再加上新的操作数乘以 256 ,此时 arg = 66048 。 读取第三条字节码指令,操作码等于 100,此时是 LOAD_CONST 这条指令,那么此时的操作码等于 arg += 65,因为操作码不是 EXTENDED_ARG 因此操作数不需要在乘以 256 了。

    上面的计算过程用程序代码表示如下,下面的代码当中 code 就是真正的字节序列 HAVE_ARGUMENT = 90 。

    def _unpack_opargs(code):
        extended_arg = 0
        for i in range(0, len(code), 2):
            op = code[i]
            if op >= HAVE_ARGUMENT:
                arg = code[i+1] | extended_arg
                extended_arg = (arg << 8) if op == EXTENDED_ARG else 0
            else:
                arg = None
            yield (i, op, arg)

    我们可以使用代码来验证我们前面的分析:

    import dis
    
    
    def num_to_byte(n):
        return n.to_bytes(1, "little")
    
    
    def nums_to_bytes(data):
        ans = b"".join([num_to_byte(n) for n in data])
        return ans
    
    
    if __name__ == '__main__':
        # extended_arg extended_num opcode oparg for python_version > 3.5
        bytecode = nums_to_bytes([144, 1, 144, 2, 100, 65])
        print(bytecode)
        dis.dis(bytecode)

    上面的代码输出结果如下所示:

    b'\x90\x01\x90\x02dA'
              0 EXTENDED_ARG             1
              2 EXTENDED_ARG           258
              4 LOAD_CONST           66113 (66113)

    根据上面程序的输出结果可以看到我们的分析结果是正确的。

    源代码字节码映射表

    在本小节主要分析一个 code object 对象当中的 co_lnotab 字段,通过分析一个具体的字段来学习这个字段的设计。

    import dis
    
    
    def add(a, b):
        a += 1
        b += 2
        return a + b
    
    
    if __name__ == '__main__':
        dis.dis(add.__code__)
        print(f"{list(bytearray(add.__code__.co_lnotab)) = }")
        print(f"{add.__code__.co_firstlineno = }")

    首先 dis 的输出第一列是字节码对应的源代码的行号,第二列是字节码在字节序列当中的位移。

    上面的代码输出结果如下所示:

      源代码的行号  字节码的位移
      6           0 LOAD_FAST                0 (a)
                  2 LOAD_CONST               1 (1)
                  4 INPLACE_ADD
                  6 STORE_FAST               0 (a)
    
      7           8 LOAD_FAST                1 (b)
                 10 LOAD_CONST               2 (2)
                 12 INPLACE_ADD
                 14 STORE_FAST               1 (b)
    
      8          16 LOAD_FAST                0 (a)
                 18 LOAD_FAST                1 (b)
                 20 BINARY_ADD
                 22 RETURN_VALUE
    list(bytearray(add.__code__.co_lnotab)) = [0, 1, 8, 1, 8, 1]
    add.__code__.co_firstlineno = 5

    从上面代码的输出结果可以看出字节码一共分成三段,每段表示一行代码的字节码。现在我们来分析一下 co_lnotab 这个字段,这个字段其实也是两个字节为一段的。比如上面的 [0, 1, 8, 1, 8, 1] 就可以分成三段 [0, 1], [8, 1], [8, 1] 。这其中的含义分别为:

    第一个数字表示距离上一行代码的字节码数目。 第二个数字表示距离上一行有效代码的行数。

    现在我们来模拟上面代码的字节码的位移和源代码行数之间的关系:

    [0, 1],说明这行代码离上一行代码的字节位移是 0 ,因此我们可以看到使用 dis 输出的字节码 LOAD_FAST ,前面的数字是 0,距离上一行代码的行数等于 1 ,代码的第一行的行号等于 5,因此 LOAD_FAST 对应的行号等于 5 + 1 = 6 。 [8, 1],说明这行代码距离上一行代码的字节位移为 8 个字节,因此第二块的 LOAD_FAST 前面是 8 ,距离上一行代码的行数等于 1,因此这个字节码对应的源代码的行号等于 6 + 1 = 7。 [8, 1],同理可以知道这块字节码对应源代码的行号是 8 。

    现在有一个问题是当两行代码之间相距的行数超过 一个字节的表示范围怎么办?在 python3.5 以后如果行数差距大于 127,那么就使用 (0, 行数) 对下一个组合进行表示,(0, \(x_1\)), (0,$ x_2$) ... ,直到 \(x_1 + ... + x_n\) = 行数。

    在后面的程序当中我们会使用 compile 这个 python 内嵌函数。当你使用Python编写代码时,可以使用compile()函数将Python代码编译成字节代码对象。这个字节码对象可以被传递给Python的解释器或虚拟机,以执行代码。

    compile()函数接受三个参数:

    source: 要编译的Python代码,可以是字符串,字节码或AST对象。 filename: 代码来源的文件名(如果有),通常为字符串。 mode: 编译代码的模式。可以是 'exec'、'eval' 或 'single' 中的一个。'exec' 模式用于编译多行代码,'eval' 用于编译单个表达式,'single' 用于编译单行代码。

    import dis
    
    code = """
    x=1
    y=2
    """ \
    + "\n" * 500 + \
    """
    z=x+y
    """
    
    code = compile(code, '', 'exec')
    print(list(bytearray(code.co_lnotab)))
    print(code.co_firstlineno)
    dis.dis(code)

    上面的代码输出结果如下所示:

    [0, 1, 4, 1, 4, 127, 0, 127, 0, 127, 0, 121]
    1
      2           0 LOAD_CONST               0 (1)
                  2 STORE_NAME               0 (x)
    
      3           4 LOAD_CONST               1 (2)
                  6 STORE_NAME               1 (y)
    
    505           8 LOAD_NAME                0 (x)
                 10 LOAD_NAME                1 (y)
                 12 BINARY_ADD
                 14 STORE_NAME               2 (z)
                 16 LOAD_CONST               2 (None)
                 18 RETURN_VALUE

    根据我们前面的分析因为第三行和第二行之间的差距大于 127 ,因此后面的多个组合都是用于表示行数的。

    505 = 3(前面已经有三行了) + (127 + 127 + 127 + 121)(这个是第二行和第三行之间的差距,这个值为 502,中间有 500 个换行但是因为字符串相加的原因还增加了两个换行,因此一共是 502 个换行)。

    具体的算法用代码表示如下所示,下面的参数就是我们传递给 dis 模块的 code,也就是一个 code object 对象。

    def findlinestarts(code):
        """Find the offsets in a byte code which are start of lines in the source.
    
        Generate pairs (offset, lineno) as described in Python/compile.c.
    
        """
        byte_increments = code.co_lnotab[0::2]
        line_increments = code.co_lnotab[1::2]
        bytecode_len = len(code.co_code)
    
        lastlineno = None
        lineno = code.co_firstlineno
        addr = 0
        for byte_incr, line_incr in zip(byte_increments, line_increments):
            if byte_incr:
                if lineno != lastlineno:
                    yield (addr, lineno)
                    lastlineno = lineno
                addr += byte_incr
                if addr >= bytecode_len:
                    # The rest of the lnotab byte offsets are past the end of
                    # the bytecode, so the lines were optimized away.
                    return
            if line_incr >= 0x80:
                # line_increments is an array of 8-bit signed integers
                line_incr -= 0x100
            lineno += line_incr
        if lineno != lastlineno:
            yield (addr, lineno)
    操作操作码
    POP_TOP1
    ROT_TWO2
    ROT_THREE3
    DUP_TOP4
    DUP_TOP_TWO5
    ROT_FOUR6
    NOP9
    UNARY_POSITIVE10
    UNARY_NEGATIVE11
    UNARY_NOT12
    UNARY_INVERT15
    BINARY_MATRIX_MULTIPLY16
    INPLACE_MATRIX_MULTIPLY17
    BINARY_POWER19
    BINARY_MULTIPLY20
    BINARY_MODULO22
    BINARY_ADD23
    BINARY_SUBTRACT24
    BINARY_SUBSCR25
    BINARY_FLOOR_DIVIDE26
    BINARY_TRUE_DIVIDE27
    INPLACE_FLOOR_DIVIDE28
    INPLACE_TRUE_DIVIDE29
    RERAISE48
    WITH_EXCEPT_START49
    GET_AITER50
    GET_ANEXT51
    BEFORE_ASYNC_WITH52
    END_ASYNC_FOR54
    INPLACE_ADD55
    INPLACE_SUBTRACT56
    INPLACE_MULTIPLY57
    INPLACE_MODULO59
    STORE_SUBSCR60
    DELETE_SUBSCR61
    BINARY_LSHIFT62
    BINARY_RSHIFT63
    BINARY_AND64
    BINARY_XOR65
    BINARY_OR66
    INPLACE_POWER67
    GET_ITER68
    GET_YIELD_FROM_ITER69
    PRINT_EXPR70
    LOAD_BUILD_CLASS71
    YIELD_FROM72
    GET_AWAITABLE73
    LOAD_ASSERTION_ERROR74
    INPLACE_LSHIFT75
    INPLACE_RSHIFT76
    INPLACE_AND77
    INPLACE_XOR78
    INPLACE_OR79
    LIST_TO_TUPLE82
    RETURN_VALUE83
    IMPORT_STAR84
    SETUP_ANNOTATIONS85
    YIELD_VALUE86
    POP_BLOCK87
    POP_EXCEPT89
    STORE_NAME90
    DELETE_NAME91
    UNPACK_SEQUENCE92
    FOR_ITER93
    UNPACK_EX94
    STORE_ATTR95
    DELETE_ATTR96
    STORE_GLOBAL97
    DELETE_GLOBAL98
    LOAD_CONST100
    LOAD_NAME101
    BUILD_TUPLE102
    BUILD_LIST103
    BUILD_SET104
    BUILD_MAP105
    LOAD_ATTR106
    COMPARE_OP107
    IMPORT_NAME108
    IMPORT_FROM109
    JUMP_FORWARD110
    JUMP_IF_FALSE_OR_POP111
    JUMP_IF_TRUE_OR_POP112
    JUMP_ABSOLUTE113
    POP_JUMP_IF_FALSE114
    POP_JUMP_IF_TRUE115
    LOAD_GLOBAL116
    IS_OP117
    CONTAINS_OP118
    JUMP_IF_NOT_EXC_MATCH121
    SETUP_FINALLY122
    LOAD_FAST124
    STORE_FAST125
    DELETE_FAST126
    RAISE_VARARGS130
    CALL_FUNCTION131
    MAKE_FUNCTION132
    BUILD_SLICE133
    LOAD_CLOSURE135
    LOAD_DEREF136
    STORE_DEREF137
    DELETE_DEREF138
    CALL_FUNCTION_KW141
    CALL_FUNCTION_EX142
    SETUP_WITH143
    LIST_APPEND145
    SET_ADD146
    MAP_ADD147
    LOAD_CLASSDEREF148
    EXTENDED_ARG144
    SETUP_ASYNC_WITH154
    FORMAT_VALUE155
    BUILD_CONST_KEY_MAP156
    BUILD_STRING157
    LOAD_METHOD160
    CALL_METHOD161
    LIST_EXTEND162
    SET_UPDATE163
    DICT_MERGE164
    DICT_UPDATE165
    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发 Python
    相关文章 更多
    精品专题 更多
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    macOS软件

    正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

    IOS软件

    正软商城iOS软件专区,精选适用于iPhone和iPad的办公、学习、影音、设计、效率及AI应用,提供功能介绍、适用设备、系统要求和正版获取方式等信息。

    AI

    正软商城AI软件专区,汇集AI写作、AI绘画、AI视频、AI办公、AI编程、AI翻译、智能客服和数据分析等人工智能工具,提供功能介绍、适用平台、收费方式及正版购买信息。

    Mac软件 更多
    Aperty macOS版
    Aperty macOS版

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    火山引擎 网页版
    火山引擎 网页版

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    立刻MV 网页版
    立刻MV 网页版

    立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

    蛙蛙写作 网页版
    蛙蛙写作 网页版

    蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

    讯飞智作 网页版
    讯飞智作 网页版

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    超级简历WonderCV 网页版
    超级简历WonderCV 网页版

    超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

    音述AI 网页版
    音述AI 网页版

    音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

    AionClaw macOS版
    AionClaw macOS版

    AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

    Loomy macOS版
    Loomy macOS版

    Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

    AirBuddy 3 macOS版
    AirBuddy 3 macOS版

    AirBuddy 3 是面向 macOS 的蓝牙外设增强工具,可集中查看 AirPods、Beats 与苹果设备电量,提供快速连接、播放控制、电量提醒、桌面小组件和多台 Mac 外设接力等功能。

    AirServer macOS版
    AirServer macOS版

    AirServer macOS版可把 Mac 转换为无线投屏接收端,通过 AirPlay 或 Google Cast 接收手机、平板和电脑画面,并支持多设备同时连接、高清镜像及会话录制,适合家庭娱乐、课堂教学、会议演示和应用展示。

    AnyDesk macOS版
    AnyDesk macOS版

    AnyDesk macOS版是一款适用于远程办公、设备维护和客户支持的远程桌面工具,可连接并操作异地计算机,支持文件传输、无人值守访问、会话权限控制及跨平台协作。

    WINDOWS 更多
    讯飞绘文 网页版
    讯飞绘文 网页版

    讯飞绘文网页版是科大讯飞推出的AI图文创作与内容运营工具,覆盖热点选题、文章生成、稿件校对、智能配图、排版发布和数据复盘等环节,适合自媒体创作者及内容团队在线使用。

    Maya(Autodesk Maya) Windows版
    Maya(Autodesk Maya) Windows版

    Autodesk Maya 是面向影视、电视、动画和游戏行业的专业三维制作软件,集多边形与 NURBS 建模、角色绑定、关键帧和动作捕捉动画、Bifrost 特效模拟、材质开发及 Arnold 渲染于一体,

    Aperty Windows版
    Aperty Windows版

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    火山引擎 网页版
    火山引擎 网页版

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    立刻MV 网页版
    立刻MV 网页版

    立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

    蛙蛙写作 网页版
    蛙蛙写作 网页版

    蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

    讯飞智作 网页版
    讯飞智作 网页版

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    超级简历WonderCV 网页版
    超级简历WonderCV 网页版

    超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

    音述AI 网页版
    音述AI 网页版

    音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

    AionClaw Windows版
    AionClaw Windows版

    AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

    Loomy Windows版
    Loomy Windows版

    Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

    AirServer Windows版
    AirServer Windows版

    AirServer Windows版可把电脑变成AirPlay、Google Cast和Miracast无线投屏接收端,适合会议演示、课堂教学及家庭大屏分享。发送设备通常无需另装客户端,并可同时接收和智能排列多个镜像会话。