当前位置:

首页 > 编程开发 > Python虚拟机字节码之装饰器怎么实现

Python虚拟机字节码之装饰器怎么实现

Python常见字节码LOAD_CONST这个指令用于将一个常量加载到栈中。常量可以是数字、字符串、元组、列表、字典等对象。例如:>>>dis.dis(lambda:42)10LOAD_CONST1(42)2RETURN_VALUELOAD_NAME这个指令用于将一个变量加载到栈中。例如:>>>dis.dis(lambda:x)10LOAD_GLOBAL0(x)2RETURN_VALUE>>>STORE_NAME这个指令用于将栈顶的值存储到一个变量中。例如:>>>dis.dis("x=42")10LOAD

本文目录

    Python 常见字节码

    LOAD_CONST

    这个指令用于将一个常量加载到栈中。常量可以是数字、字符串、元组、列表、字典等对象。例如:

    >>> dis.dis(lambda: 42)
      1           0 LOAD_CONST               1 (42)
                  2 RETURN_VALUE

    LOAD_NAME

    这个指令用于将一个变量加载到栈中。例如:

    >>> dis.dis(lambda: x)
      1           0 LOAD_GLOBAL              0 (x)
                  2 RETURN_VALUE
    >>>

    STORE_NAME

    这个指令用于将栈顶的值存储到一个变量中。例如:

    >>> dis.dis("x=42")
      1           0 LOAD_CONST               0 (42)
                  2 STORE_NAME               0 (x)
                  4 LOAD_CONST               1 (None)
                  6 RETURN_VALUE

    BINARY_ADD

    这个指令用于对栈顶的两个值进行加法运算并将结果推送到栈中。

    >>> dis.dis(lambda: x + y)
      1           0 LOAD_GLOBAL              0 (x)
                  2 LOAD_GLOBAL              1 (y)
                  4 BINARY_ADD
                  6 RETURN_VALUE

    BINARY_SUBTRACT

    这个指令用于对栈顶的两个值进行减法运算并将结果推送到栈中。

    >>> dis.dis(lambda: x - y)
      1           0 LOAD_GLOBAL              0 (x)
                  2 LOAD_GLOBAL              1 (y)
                  4 BINARY_SUBTRACT
                  6 RETURN_VALUE

    同样的加减乘除取余数的字节码如下所示:

    >>> dis.dis(lambda: x + y)
      1           0 LOAD_GLOBAL              0 (x)
                  2 LOAD_GLOBAL              1 (y)
                  4 BINARY_ADD
                  6 RETURN_VALUE
    >>> dis.dis(lambda: x - y)
      1           0 LOAD_GLOBAL              0 (x)
                  2 LOAD_GLOBAL              1 (y)
                  4 BINARY_SUBTRACT
                  6 RETURN_VALUE
    >>> dis.dis(lambda: x * y)
      1           0 LOAD_GLOBAL              0 (x)
                  2 LOAD_GLOBAL              1 (y)
                  4 BINARY_MULTIPLY
                  6 RETURN_VALUE
    >>> dis.dis(lambda: x / y)
      1           0 LOAD_GLOBAL              0 (x)
                  2 LOAD_GLOBAL              1 (y)
                  4 BINARY_TRUE_DIVIDE
                  6 RETURN_VALUE
    >>> dis.dis(lambda: x // y)
      1           0 LOAD_GLOBAL              0 (x)
                  2 LOAD_GLOBAL              1 (y)
                  4 BINARY_FLOOR_DIVIDE
                  6 RETURN_VALUE
    >>> dis.dis(lambda: x % y)
      1           0 LOAD_GLOBAL              0 (x)
                  2 LOAD_GLOBAL              1 (y)
                  4 BINARY_MODULO
                  6 RETURN_VALUE

    COMPARE_OP

    这个指令用于比较栈顶的两个值,并且将比较得到的结果压入栈中,这个字节码后面后一个字节的参数,表示小于大于不等于等等比较符号。例如:

    >>> dis.dis(lambda: x - y)
      1           0 LOAD_GLOBAL              0 (x)
                  2 LOAD_GLOBAL              1 (y)
                  4 BINARY_SUBTRACT
                  6 RETURN_VALUE
    >>> dis.dis(lambda: x > y)
      1           0 LOAD_GLOBAL              0 (x)
                  2 LOAD_GLOBAL              1 (y)
                  4 COMPARE_OP               4 (>)
                  6 RETURN_VALUE
    >>> dis.dis(lambda: x < y)
      1           0 LOAD_GLOBAL              0 (x)
                  2 LOAD_GLOBAL              1 (y)
                  4 COMPARE_OP               0 (<)
                  6 RETURN_VALUE
    >>> dis.dis(lambda: x != y)
      1           0 LOAD_GLOBAL              0 (x)
                  2 LOAD_GLOBAL              1 (y)
                  4 COMPARE_OP               3 (!=)
                  6 RETURN_VALUE
    >>> dis.dis(lambda: x <= y)
      1           0 LOAD_GLOBAL              0 (x)
                  2 LOAD_GLOBAL              1 (y)
                  4 COMPARE_OP               1 (<=)
                  6 RETURN_VALUE
    >>> dis.dis(lambda: x >= y)
      1           0 LOAD_GLOBAL              0 (x)
                  2 LOAD_GLOBAL              1 (y)
                  4 COMPARE_OP               5 (>=)
                  6 RETURN_VALUE
    >>> dis.dis(lambda: x == y)
      1           0 LOAD_GLOBAL              0 (x)
                  2 LOAD_GLOBAL              1 (y)
                  4 COMPARE_OP               2 (==)
                  6 RETURN_VALUE

    RETURN_VALUE

    将栈顶元素弹出作为返回值。

    BUILD_LIST

    这个指令用于创建一个列表。例如:

    >>> dis.dis(lambda: [a, b, c, e])
      1           0 LOAD_GLOBAL              0 (a)
                  2 LOAD_GLOBAL              1 (b)
                  4 LOAD_GLOBAL              2 (c)
                  6 LOAD_GLOBAL              3 (e)
                  8 BUILD_LIST               4
                 10 RETURN_VALUE

    这条字节码指令有一个参数表示栈空间当中列表元素的个数,在上面的例子当中这个参数是 4 。

    BUILD_TUPLE

    这个指令用于创建一个元组。例如:

    >>> dis.dis(lambda: (a, b, c))
      1           0 LOAD_GLOBAL              0 (a)
                  2 LOAD_GLOBAL              1 (b)
                  4 LOAD_GLOBAL              2 (c)
                  6 BUILD_TUPLE              3
                  8 RETURN_VALUE

    同样的这个字节码也有一个参数,表示创建元组的元素个数。

    BUILD_MAP

    这个指令用于创建一个字典。例如:

    BUILD_SET

    和 list 和 tuple 一样,这条指令是用于创建一个集合对象,同样的这条指令也有一个参数表示用于创建集合的元素的个数。

    >>> dis.dis(lambda: {a, b, c, d})
      1           0 LOAD_GLOBAL              0 (a)
                  2 LOAD_GLOBAL              1 (b)
                  4 LOAD_GLOBAL              2 (c)
                  6 LOAD_GLOBAL              3 (d)
                  8 BUILD_SET                4
                 10 RETURN_VALUE

    BUILD_CONST_KEY_MAP

    这条指令是用于创建一个字典对象,同样的这条指令也有一个参数,表示字典当中元素的个数。

    >>> dis.dis(lambda: {1:2, 3:4})
      1           0 LOAD_CONST               1 (2)
                  2 LOAD_CONST               2 (4)
                  4 LOAD_CONST               3 ((1, 3))
                  6 BUILD_CONST_KEY_MAP      2
                  8 RETURN_VALUE

    从字节码角度分析装饰器的原理

    如果你是一个 pythoner 那么你肯定或多或少听说过装饰器,这是一个 python 的语法糖我们可以用它来做很多有趣的事情,比如在不修改源代码的基础之上给函数附加一些功能,比如说计算时间。

    import time
     
    def eval_time(func):
        
        def cal_time(*args, **kwargs):
            start = time.time()
            r = func(*args, **kwargs)
            end = time.time()
            return r, end - start
        return cal_time
     
     
    @eval_time
    def fib(n):
        a = 0
        b = 1
        while n > 0:
            n -= 1
            a, b = b, a + b
        return a

    在上面的代码当中我们实现了一个计算斐波拉契数列的函数,除此之外还写了一个 eval_time 函数用于计算函数执行的时间,现在调用函数 fib(10),程序的输出如下所示:

    >>>fib(10)
    (55, 5.9604644775390625e-06)

    可以看到实现了我们想要的效果。

    现在我们使用一个更加简单的例子来模拟上面的代码结构,方便我们对上面函数执行的过程进行分析:

    s = """
    def decorator(func):
        print("Hello")
        return func
     
    @decorator
    def fib(n):
        pass
    """
    dis.dis(s)

    上面的 dis 函数的输出对应代码的字节码如下所示:

      2           0 LOAD_CONST               0 (", line 2>)
                  2 LOAD_CONST               1 ('decorator')
                  4 MAKE_FUNCTION            0
                  6 STORE_NAME               0 (decorator)
     
      6           8 LOAD_NAME                0 (decorator)
     
      7          10 LOAD_CONST               2 (", line 6>)
                 12 LOAD_CONST               3 ('fib')
                 14 MAKE_FUNCTION            0
                 16 CALL_FUNCTION            1
                 18 STORE_NAME               1 (fib)
                 20 LOAD_CONST               4 (None)
                 22 RETURN_VALUE
     
    Disassembly of ", line 2>:
      3           0 LOAD_GLOBAL              0 (print)
                  2 LOAD_CONST               1 ('Hello')
                  4 CALL_FUNCTION            1
                  6 POP_TOP
     
      4           8 LOAD_FAST                0 (func)
                 10 RETURN_VALUE
     
    Disassembly of ", line 6>:
      8           0 LOAD_CONST               0 (None)
                  2 RETURN_VALUE

    执行第一条指令 LOAD_CONST,这条指令主要是加载一个 code object 对象,这个对象里面主要是包含函数 decorator 的字节码,主要是上面字节码的第二块内容。在执行完这条字节码之后栈空间如下所示:

    Python虚拟机字节码之装饰器怎么实现

    执行完第二条指令 LOAD_CONST 之后,会将字符串 decorator 加载进入栈空间当中。

    Python虚拟机字节码之装饰器怎么实现

    执行第三条指令 MAKE_FUNCTION,这条字节码的作用是在虚拟机内部创建一个函数,函数的名称为 decorator,函数对应的字节码则是在先前压入栈空间当中的 code object 对象,这条指令还会将创建好的函数对象压入栈中。

    Python虚拟机字节码之装饰器怎么实现

    STORE_NAME,条字节码会将栈顶的元素弹出,并且将 co_names[oparg] 指向这个对象,在上面的字节码当中 co_names[oparg] 就是 decorator 。

    Python虚拟机字节码之装饰器怎么实现

    LOAD_NAME,这条字节码就是将 co_names[oparg] 对应的名字指向的对象重新加载进入栈空间当中,也就是上面的 decorator 函数加入进行栈空间当中。

    Python虚拟机字节码之装饰器怎么实现

    接下来的三条字节码 LOAD_CONST,LOAD_CONST 和 MAKE_FUNCTION,在执行这三条字节码之后,栈空间如下所示:

    Python虚拟机字节码之装饰器怎么实现

    接下来的一条指令非常重要,这条指令便是装饰器的核心原理,CALL_FUNCTION 这条指令有一个参数 i,在上面的字节码当中为 1,也就是说从栈顶开始的前 i 个元素都是函数参数,调用的函数在栈空间的位置为 i + 1 (从栈顶往下数),那么在上面的情况下就是说调用 decorator 函数,并且将 fib 函数作为 decorator 函数的参数,decorator 函数的返回值再压入栈顶。在上面的代码当中 decorator 函数返回值也是一个函数,也就是 decorator 函数的参数,即 fib 函数。

    Python虚拟机字节码之装饰器怎么实现

    接下来便是 STORE_NAME 字节码,这条字节码的含义我们在前面已经说过了,就是将栈顶元素弹出,保存到 co_names[oparg] 指向的对象当中,在上面的代码当中也就是将栈顶的对象保存到 fib 当中。栈顶元素 fib 函数是调用函数 decorator 的返回值。

    看到这里就能够理解了原来装饰器的最根本的原理不就是函数调用嘛,比如我们最前面的用于计算函数执行时间的装饰器的原理就是:

    fib = eval_time(fib)

    将 fib 函数作为 eval_time 函数的参数,再将这个函数的返回值保存到 fib 当中,当然这个对象必须是可调用的,不然后面使用 fib() 就会保存,我们可以使用下面的代码来验证这个效果。

    def decorator(func):
        return func()
     
     
    @decorator
    def demo():
        return "function demo return string : Demo"
     
    print(demo)

    执行上面的程序结果为:

    function demo return string : Demo

    可以看到 demo 已经变成了一个字符串对象而不再是一个函数了,因为 demo = decorator(demo),而在函数 decorator 当中返回值是 demo 函数自己的返回值,因此才打印了字符串。

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发 Python
    相关文章 更多
    python删除一个列表里的重复元素的方法与代码示例
    python删除一个列表里的重复元素的方法与代码示例

    学习如何使用Python高效删除列表中的重复元素。本文提供5种常见方法的代码示例,涵盖保持顺序与不保持顺序的场景,适合初学者和进阶开发者参考。

    Python写接口的步骤及完整代码示例
    Python写接口的步骤及完整代码示例

    详细讲解使用Python编写Web接口的核心步骤与完整代码示例,涵盖环境准备、路由定义、数据校验、服务启动及调试测试方法。

    codex怎么读取word文档及docx文件操作教程
    codex怎么读取word文档及docx文件操作教程

    介绍如何利用编程工具读取和解析Word文档(docx格式),涵盖基础文本提取代码示例、结构化数据处理逻辑,以及处理复杂排版时的技术局限与替代方案。

    怎么在Python中调用C语言代码提升性能
    怎么在Python中调用C语言代码提升性能

    详细讲解在Python中调用C语言代码提升性能的完整实践指南,包含原理分析、编译步骤、ctypes与CFFI具体实现、性能对比及常见陷阱。

    Python与C语言的性能差异及底层原理
    Python与C语言的性能差异及底层原理

    深入解析Python与C语言在执行效率上的数量级差异,从编译机制、变量内存布局、动态类型推断到GIL锁,系统阐明底层核心原理。

    Python和C语言哪个更适合编程入门
    Python和C语言哪个更适合编程入门

    客观对比Python与C语言在心智负担、底层机制与学习反馈上的核心差异,提供清晰有据的选择标准与切实的入门学习路径。

    selenium上传文件教程本地文件路径设置方法
    selenium上传文件教程本地文件路径设置方法

    深入解析Selenium处理文件上传的技术细节,涵盖标准HTML控件的路径直接注入法与非标准控件的系统级模拟方案,提供Windows与Linux环境下的路径设置最佳实践及常见异常排查。

    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景
    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景

    针对大数据工程场景,系统对比Java、Scala与Python在执行性能、生态结合、开发效率和维护成本上的核心差异,给出清晰的架构选型依据与落地方案。

    langgraph python guide中文入门教程详解
    langgraph python guide中文入门教程详解

    本篇LangGraph Python中文入门教程详解,系统梳理StateGraph状态机定义、节点编写、边与条件路由、检查点持久化(MemorySaver)等核心机制,提供从环境安装到完整循环智能体构建的代码示例与实操指导。

    Windows 安装 Python:从官方入口到虚拟环境的可复现实践
    Windows 安装 Python:从官方入口到虚拟环境的可复现实践

    面向后端开发者,基于 Python 官方文档说明如何在 Windows 上安装 Python、创建 venv 虚拟环境及使用 pip 管理依赖。包含可复制命令、验证方法及工具选择边界,避免常见配置误区。

    查看更多
    精品专题 更多
    装机必备
    装机必备

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

    Windows
    Windows

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

    PDF教程
    PDF教程

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

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

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

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

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

    Aperty macOS版
    Aperty macOS版
    macOS

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

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

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

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

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

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。

    为什么要购买正版软件?
    01 官方授权

    授权来源透明,订单与许可信息可随时查询。

    02 即时交付

    付款成功后自动发放激活信息,支持订单追踪。

    03 中文协助

    安装、激活、迁移与续费问题都有专人响应。

    04 长期可用

    提供升级提醒与授权管理,换机时也能轻松迁移。

    网站备案号:苏ICP备2026018738号-1 联系邮箱:bd@zhengruan.com 网站地图

    Copyright ©2018-2026