Python 装饰器与函数调用机制(复习笔记 · 2026-07-07)

一、最基础:def 是定义,() 是调用

  • def say_hello(): print("Hello!") ------ 只定义,不执行。函数体被"存起来",不打印任何东西。
  • say_hello() ------ 有括号 = 调用 = 真正执行,才会打印 "Hello!"。
  • 如果删掉调用那行,程序什么都不输出(函数只定义、从没被喊去跑)。

❗ 误区:以为 @装饰器 会自动让函数运行。不会,必须显式写 名字() 才执行。

二、无参装饰器:名字被重新贴到 wrapper 上

复制代码
def my_decorator(func):
    def wrapper():
        print("函数执行前")
        func()
        print("函数执行后")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()

执行流程

  1. def my_decorator:定义装饰器函数(不跑)。

  2. def say_hello:定义原函数(不跑)。

  3. @my_decorator 等价于 say_hello = my_decorator(say_hello)

    • 调用 my_decorator,传入原函数;
    • 内部定义 wrapper(不跑),return wrapper 把 wrapper 交出去;
    • say_hello 这个名字现在指向 wrapper(不再是原函数)。
  4. say_hello() 实际执行 wrapper()

    函数执行前
    Hello!
    函数执行后

关键结论

  • @装饰器 本质 = 函数名 = 装饰器(函数名),把名字重新绑定到新函数。
  • 装饰器执行时(@那行)只定义和返回,不打印;打印发生在你后续调用时。
  • 没有调用那行 → 什么都不输出。

三、Python 的"指向"不是 C 指针(重要纠偏)

❗ 当时类比错了的地方,已全部纠正:

|----------------------|-------------------------------------------------------|
| 原错误类比 | 正确概念 |
| 方法是内存块,指针从上往下扫到哪执行到哪 | def 语句本身执行了(创建函数对象),但函数体不执行,只是存起来等调用 |
| 指向像 C 指针,知道内存哪块就跳去执行 | Python 名字是引用/reference (标签贴对象),不是 C 指针,不能指针运算 |
| 占多少内存要关心 | 函数对象很小(几百字节),Python 有自动 GC,学习阶段不用管内存 |
| Python 有指针 | 没有指针;有调用栈、有 list(动态数组)、有 numpy.ndarray(AI 必用) |

Java 学习者对照表(学过 Java,基本能直接迁移)

|---------------------------|-------------------------------|
| Java 认知 | Python 对应 |
| 方法定义不运行,要 obj.method() 才调 | ✅ 一样,def 不跑,func() 才跑 |
| 对象靠引用、有 GC、有调用栈/堆 | ✅ Python 一样,且一切皆对象(连函数都是) |
| primitive 在栈、对象在堆 | Python 更统一:全是对象、全靠引用 |
| 无裸指针、不能指针运算 | ✅ Python 也没有 |
| 要 new 才创建对象 | Python 不用 new,赋值即创建 |
| 要手动 free/delete | Python 自动 GC,不用管 |

唯一要"忘掉"的:C 的手动内存管理 + 指针算术。Python 比 Java 还省心。

四、(*args, **kwargs):装饰器的"万能接口"

复制代码
def wrapper(*args, **kwargs):
    result = original_function(*args, **kwargs)
    return result
  • 调用 target_function()(空括号)→ args=() 空元组、kwargs={} 空字典,啥都没接
  • target_function(1, 2, name="小澪")args=(1,2)kwargs={'name':'小澪'}

|------------|-------------|----------|
| 写法 | 接住 | 存为 |
| *args | 位置参数(按顺序的值) | 元组 tuple |
| **kwargs | 关键字参数(名字=值) | 字典 dict |

  • 定义处的 * / ** = 收集(打包收到的参数)。
  • 调用处的 original_function(*args, **kwargs)* / ** = 解包(摊开传回去)。
  • 作用:让装饰器通用,不管被装饰函数要不要参数都能透传。无参函数传空进去正好。

❗ 误区:以为 (*args, **kwargs) 是"必须填两个"。不是,是"能接住任意参数"的兜底,不传就都是空的。

五、带参装饰器(装饰器工厂)= 多一层

复制代码
def repeat(num_times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                func(*args, **kwargs)
        return wrapper
    return decorator

@repeat(3)
def say_hello():
    print("Hello!")

say_hello()

三层接参(最关键,别混)

|-------|----------------------------|-----------------|---------------|
| | 函数 | 接住 | 来自 |
| 1 | repeat(num_times) | 配置参数 3 | repeat(3) |
| 2 | decorator(func) | 被装饰函数 say_hello | @ 语法 |
| 3 | wrapper(*args, **kwargs) | 调用时参数 | say_hello() |

❗ 当时误解:@repeat(3) 把 say_hello 传给了 repeat。错!

  • repeat(3) 先算 → 传 num_times=3 → 返回 decorator(内部记住 num_times=3)。
  • 然后 @decorator 才把 say_hello 传给 decoratorfunc
  • 不是同一个函数接的两个东西,是先吃配置、再吐出一个定制装饰器去接函数。

对比无参装饰器

|------------------|-------------------------------------------|
| 无参 @df | 带参 @repeat(3) |
| df 直接接原函数 | repeat 先接 3 → 吐 decorator;decorator 再接原函数 |
| 两层(df / wrapper) | 三层(repeat / decorator / wrapper) |

多出的 repeat 层唯一作用:接收配置参数,返回真正能装饰函数的 decorator → 所以叫"装饰器工厂"。

六、for _ in range(n):循环 n 次

  • range(3) 产生 0, 1, 2(从 0 到 n-1,共 n 个)。
  • for _ in range(n): = "循环 n 次",每轮执行缩进块。
  • _ 是占位变量名,表示"这个数我不用",纯占位(写 for i in ... 也一样,只是 i 没被用)。
  • 带参装饰器打印三次,就是 for _ in range(num_times)num_times=3 决定的;改成 5 就打印 5 次。
  • range() 不是列表,是惰性序列,省内存,学习阶段只需知道"range(n) = 0 到 n-1 共 n 个数"。

七、今日核心心法(背下来)

  1. def 造函数不跑,名字() 才跑。
  1. @装饰器 = 函数名 = 装饰器(函数名),名字被重新指向新函数。
  1. 没写调用 → 不输出;要输出必须显式 名字()
  1. 名字是引用不是指针,内存 Python 自动管。
  1. (*args, **kwargs) 是装饰器万能接口,定义处收集、调用处解包。
  1. 带参装饰器多一层"工厂":先收配置、再产装饰器、最后接函数。
  1. range(n) + for = 循环 n 次;_ 是占位变量。
相关推荐
浩瀚地学1 小时前
【Java基础复习】IO流(二)
java·开发语言·经验分享·笔记·学习
^yi1 小时前
【C++】内存管理
开发语言·c++
AC赳赳老秦1 小时前
采购专员自动化:OpenClaw 自动比价、生成询价单、跟踪供应商报价进度实战指南
开发语言·数据库·人工智能·python·自动化·github·openclaw
QiLinkOS1 小时前
QiLink OS的失败数据共享平台的运作模式是否适用于所有行业?
android·开发语言·人工智能·算法·重构·开源
CRMEB定制开发1 小时前
企业级Java电商系统选型路线图:从零到上线全流程拆解
java·开发语言·商城系统·小程序商城
Zhou1411361 小时前
Java常见面试题3
java·开发语言
云栖梦泽2 小时前
从 IMX415 摄像头驱动理解 Media Controller 框架
linux·开发语言·c++·嵌入式硬件
ednO8nqdR2 小时前
Python小游戏制作:如何实现可配置的跨分辨率界面布局
python·plotly·django·virtualenv·scikit-learn·fastapi·pygame
初阳7852 小时前
【Qt】界面优化(1)——QSS
开发语言·qt