码农高天拆解字节码 厘清Python装饰器仅为函数重绑定语法糖

【python】装饰器超详细教学,用尽毕生所学给你解释清楚,以后再也不迷茫了! — 码农高天

码农高天

Python装饰器在底层完全等价于将函数对象传参后重新赋值给原变量名,带参数版本也仅是在此基础上增加了一层中间函数调用。

AI-assisted summary of 【python】装饰器超详细教学,用尽毕生所学给你解释清楚,以后再也不迷茫了! — original source. Check the original for full context.

Source published:

Key ideas

  • Python 中的函数本质上是普通的函数对象(function object),定义函数仅是新建变量保存可调用的代码对象,因此函数能像普通变量一样作为参数传入或作为返回值返回。 (0:00)
  • 装饰器在底层完全等价于语法糖 `func = dec(func)`,其核心是接收一个可调用对象并返回一个被重新赋值给原函数名的新对象。 (5:00)
  • 带参数的装饰器结构并未引入新魔法,仅是通过三层嵌套函数增加了一次前置调用,等价逻辑展开为 `func = dec(arg)(func)`。 (10:00)

Detailed notes

## Background 该视频由“码农高天”于 2023 年 9 月 29 日发布在 YouTube 平台上。视频简介表明这是一期针对 Python 中较易令人混淆的核心知识点——装饰器(decorator)的深度教学,旨在通过尽可能覆盖所有细节的讲解,帮助观众彻底消除使用装饰器时的困惑与迷茫。

## TL;DR 视频首先从 Python 底层机制切入,强调“函数即对象”的概念,阐明函数本质上是保存在变量中的可调用对象,因此可以自然地作为参数传递或作为返回值返回。随后,作者通过分析字节码和演示“装饰器直接返回数字 1”的极端反例,证明装饰器在本质上不过是 `double = dec(double)` 这一形式的语法糖,并结合 `*args` 与 `**kwargs` 演示了标准计时包装器的实现。最后,视频拆解了进阶的带参装饰器,指出其核心只是多了一层接收配置参数的函数调用,通过三层嵌套结构即可清晰理清其执行脉络。

## Atmosphere & style 整期视频节奏明快且逻辑极其严密,采用“破除玄学”的硬核技术流风格,通过直接展示底层字节码和刻意编写极端反例,迅速拉近了高阶抽象语法与直观代码逻辑之间的距离。主讲人的语调沉稳笃定且循循善诱,既有刨根问底的技术纯粹感,又保持着对初学者友好的亲和力,给观众带来一种复杂知识点被层层剥开、豁然开朗的透彻与踏实感。

## Bulletin - [00:00](https://www.youtube.com/watch?v=GSdEK-JTKiw&t=0s) Python 中的函数本质上是普通的函数对象(function object),定义函数仅是新建变量保存可调用的代码对象,因此函数能像普通变量一样作为参数传入或作为返回值返回。 - [05:00](https://www.youtube.com/watch?v=GSdEK-JTKiw&t=300s) 装饰器在底层完全等价于语法糖 `func = dec(func)`,其核心是接收一个可调用对象并返回一个被重新赋值给原函数名的新对象。 - [10:00](https://www.youtube.com/watch?v=GSdEK-JTKiw&t=600s) 带参数的装饰器结构并未引入新魔法,仅是通过三层嵌套函数增加了一次前置调用,等价逻辑展开为 `func = dec(arg)(func)`。

## Key takeaways - [00:00](https://www.youtube.com/watch?v=GSdEK-JTKiw&t=0s) 在 Python 中一切皆为对象,函数也是普通对象,在底层通过 `code object` 经由 `MAKE_FUNCTION` 构造成 `function object` 并赋值给变量名。 - [00:45](https://www.youtube.com/watch?v=GSdEK-JTKiw&t=45s) 函数对象具备 callable(可调用)特性,语法上支持在名称后添加小括号 `()` 执行,对非 callable 对象发起调用会在运行时报错。 - [01:30](https://www.youtube.com/watch?v=GSdEK-JTKiw&t=90s) 函数可以作为高阶函数的参数传递,例如将具体算子函数注入到通用计算函数中执行。 - [02:30](https://www.youtube.com/watch?v=GSdEK-JTKiw&t=150s) 函数可以作为另一个函数的返回值被传出,返回出的内部闭包函数依然保留可调用特性。 - [05:00](https://www.youtube.com/watch?v=GSdEK-JTKiw&t=300s) 装饰器的本质就是语法糖,在语法层面上 `@dec` 修饰的函数定义完全等价于 `func = dec(func)`。 - [05:30](https://www.youtube.com/watch?v=GSdEK-JTKiw&t=330s) 字节码层面证实,装饰器先完成目标函数的标准定义与构造,再紧接着加载装饰器并执行 `CALL_FUNCTION`,最后将结果存回原函数变量名。 - [06:00](https://www.youtube.com/watch?v=GSdEK-JTKiw&t=360s) 装饰器甚至可以直接返回非函数对象(如直接返回数字 1),被修饰后的函数名就会变成纯数字,证明其并无特殊黑魔法限制。 - [07:00](https://www.youtube.com/watch?v=GSdEK-JTKiw&t=420s) 常规装饰器通常返回一个内部 `wrapper` 包装函数,在内部执行附加逻辑(如统计耗时)后再执行原函数并返回结果。 - [08:00](https://www.youtube.com/watch?v=GSdEK-JTKiw&t=480s) 在 `wrapper` 内部使用 `*args` 和 `**kwargs` 可以确保装饰器兼容被修饰函数的任意形参列表和签名。 - [10:00](https://www.youtube.com/watch?v=GSdEK-JTKiw&t=600s) 带参数装饰器在语法糖展开时等价于两步调用 `func = timing(1000)(func)`,即先带参调用生成常规装饰器,再修饰目标函数。 - [10:30](https://www.youtube.com/watch?v=GSdEK-JTKiw&t=630s) 带参数装饰器由三层嵌套构成:最外层接收配置参数、中间层接收被装饰函数、最内层接管实际执行逻辑。

## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=GSdEK-JTKiw&t=0s) 理解装饰器的前置知识:Python 中的函数即对象与高阶函数

很多初学 Python 的人对装饰器(Decorator)感到十分迷茫,主要原因是许多其他编程语言并没有原生提供这类语法,而且网络上的教程和示例代码往往非常复杂,让人望而却步。要把装饰器彻底掰开揉碎讲明白,首先必须理清 Python 的基础知识:在 Python 中,所有东西都是对象(object),函数也不例外。

从 Python 字节码的角度来看,定义一个函数并没有任何神秘之处。函数体本身会被编译为一个 `code object`(代码对象),用于保存底层的运行逻辑。随后的字节码会把该 `code object` 与函数名 `double` 加载(LOAD)进来,并通过 `MAKE_FUNCTION` 将其构造成一个 `function object`(函数对象),最后将这个函数对象存储(STORE)到变量名 `double` 中。

“所以说当我们在python中所谓定义一个函数的时候我们只是新建了一个变量然后这个变量里面保存了一个函数对象也就是function object”——码农高天。

函数对象的一个关键特征在于它是“可调用的”(callable)。在 Python 语法层面,callable 意味着名字后面可以跟一对小括号 `()` 来发起调用。只要对象本身不是 callable(例如尝试调用数字 `1()`),虽然能编译通过且会给出 `SyntaxWarning`,但在运行时(runtime)依然会报错。

理解了函数只是普通对象后,自然就能理解函数可以像普通变量一样被传递: 1. **函数作为参数**:定义一个 `calc_number(func, num)`,该函数接收一个函数作为第一个参数、一个数字作为第二个参数,然后在内部执行 `func(num)`。将 `double`(乘 2)和 `triple`(乘 3)作为参数传入并给入数字 3,就会分别打印出 6 和 9。 2. **函数作为返回值**:函数不仅能被传进去,还可以作为返回值被传出来。例如设计一个 `get_multiple_func(n)`,它接收一个参数 `n`,并在内部定义并返回一个内部函数 `multiple(x)`(返回 `n * x`)。当调用 `get_multiple_func(2)` 时,返回的就是倍增函数;传入 3 时,返回的就是三倍函数。返回出来的函数依然是 callable,可以像普通函数一样接收参数执行。

```mermaid flowchart LR accTitle: 函数作为返回值的数据流向 accDescr: 展示外部函数接收参数后返回内部闭包函数并被调用的全流程

subgraph Outer["外部高阶函数"] A["get_multiple_func(n=2)"] --> B["生成并返回 multiple(x)"] end subgraph Execution["外部调用过程"] B --> C["变量 double 获取内部函数"] C --> D["double(3) 触发调用"] D --> E["输出结果: 6"] end ```

---

[05:00](https://www.youtube.com/watch?v=GSdEK-JTKiw&t=300s) 装饰器的本质:语法糖与极端反例

一旦理解了“函数可以作为参数传给另一个函数”以及“函数的返回值也可以是一个函数”,装饰器就变得异常清晰。装饰器本身就是一个普通的 callable,在绝大多数情况下,`@` 后面跟着的名称就是一个函数名。

在函数 `double` 的定义上方添加 `@dec`,查看其底层的字节码可以发现:上半部分依然是正常的函数定义与 `MAKE_FUNCTION`,而下半部分则是装饰器真正生效的逻辑。底层的操作是先加载装饰器函数,紧接着执行 `CALL_FUNCTION`,再把结果 `STORE_NAME` 回到 `double`。

“当我们对一个函数做一个decorator的操作的时候它完全等价于这个变量名等于Decorator调用这个变量名也就是说Decorator不过是一个输入和输出都是函数的函数”——码农高天。

为了消除大家对装饰器的敬畏感,码农高天举了一个极端的反例:如果写一个装饰器函数 `dec(f)`,里面直接 `return 1`,并在 `double` 上标注 `@dec`,最后直接打印该变量 `print(double)`,输出结果就是一个纯粹的数字 `1`。这证明了装饰器在语法层面根本不具有任何神秘性,它完完全全等价于 `double = dec(double)`。

在正常的开发场景中,装饰器通常是接收一个函数作为参数,并返回一个新的包装函数(wrapper)。以性能计时器 `timing` 为例: - `timing(f)` 接收被修饰的函数 `f`。 - 其内部定义了一个 `wrapper(*args, **kwargs)` 函数。在 wrapper 内部先记录起始时间,调用被装饰的原函数 `f(*args, **kwargs)` 并保存返回值,记录结束时间并打印耗时,最后将原函数的返回值返回。 - 借助 `*args` 和 `**kwargs`(可变位置参数和关键字参数),可以让装饰器兼容任意签名、任意参数数量的函数,使 `timing` 可以无缝复用到各种不同的业务函数上。

---

[10:00](https://www.youtube.com/watch?v=GSdEK-JTKiw&t=600s) 进阶:带参数的装饰器实现逻辑

很多人在阅读进阶代码时,常被“带参数的装饰器”绕晕。其实带参数的装饰器并没有任何奇特之处,它只是在等价转换的过程中**多了一次函数调用**。

对比两者在代码展开时的等价形式: - **无参数装饰器**(`@timing`): ```python double = timing(double) ``` - **带参数装饰器**(`@timing(1000)`): ```python double = timing(1000)(double) ```

“原来这个Decorator是参数是函数返回值也是函数现在这个decorator是参数是可以你任意指定的东西然后他要返回一个函数A 这个函数A的参数是函数 返回值也是函数”——码农高天。

带参数装饰器的具体嵌套结构可拆解为三层: 1. **最外层**(如 `timeit(iterations)`):接收用户自定义的配置参数(如执行循环测试的次数 `iterations`)。它的返回值是一个函数,即 `inner`。 2. **中间层**(即 `inner(f)`):它充当了原本没有参数时的那个经典装饰器,接收待装饰的目标函数 `f` 作为参数,返回实际替换原函数的 `wrapper`。 3. **最内层**(即 `wrapper(*args, **kwargs)`):实际接管执行逻辑。在内部循环运行 `iterations` 次函数 `f`,统计总耗时并返回结果。

```mermaid flowchart TD accTitle: 带参数装饰器的三层执行流程 accDescr: 展示带参装饰器从接收配置到返回执行包装器的三层嵌套过程

subgraph Level1["第一层: 接收配置参数"] A["调用 timing(1000)"] --> B["返回第二层函数 inner"] end subgraph Level2["第二层: 接收被修饰函数"] B --> C["inner(double) 接收原函数"] C --> D["返回第三层函数 wrapper"] end subgraph Level3["第三层: 实际执行调用"] D --> E["赋回原函数名: double = wrapper"] E --> F["double(x) 触发 wrapper 内部循环执行 1000 次"] end ```

通过这一层层展开,原本复杂的装饰器逻辑就完全还原成了 Python 基础的函数调用链条。只要牢记核心等价公式与函数对象的本质,面对多层嵌套或带参数的装饰器时便不再会感到困惑。

## Notable quotes > [05:00](https://www.youtube.com/watch?v=GSdEK-JTKiw&t=300s) 当我们对一个函数做一个decorator的操作的时候它完全等价于这个变量名等于Decorator调用这个变量名也就是说Decorator不过是一个输入和输出都是函数的函数 — 码农高天

> [00:00](https://www.youtube.com/watch?v=GSdEK-JTKiw&t=0s) 当我们在python中所谓定义一个函数的时候我们只是新建了一个变量然后这个变量里面保存了一个函数对象也就是function object — 码农高天

## Mentioned Python, 码农高天

## Video description 这期做一个教学向的视频,之前有人想让我讲一下装饰器decorator,这也确实是python里比较让人迷惑的一个知识点。这次有个机会把里面的所有细节都尽可能地触及到,希望大家看完视频就再也不为怎么用装饰器发愁了!

[Source](https://www.youtube.com/watch?v=GSdEK-JTKiw)

中文简报

码农高天拆解字节码 厘清Python装饰器仅为函数重绑定语法糖 Python装饰器在底层完全等价于将函数对象传参后重新赋值给原变量名,带参数版本也仅是在此基础上增加了一层中间函数调用。 视频首先从 Python 底层机制切入,强调“函数即对象”的概念,阐明函数本质上是保存在变量中的可调用对象,因此可以自然地作为参数传递或作为返回值返回。随后,作者通过分析字节码和演示“装饰器直接返回数字 1”的极端反例,证明装饰器在本质上不过是 `double = dec(double)` 这一形式的语法糖,并结合 `*args` 与 `**kwargs` 演示了标准计时包装器的实现。最后,视频拆解了进阶的带参装饰器,指出其核心只是多了一层接收配置参数的函数调用,通过三层嵌套结构即可清晰理清其执行脉络。 Python 中的函数本质上是普通的函数对象(function object),定义函数仅是新建变量保存可调用的代码对象,因此函数能像普通变量一样作为参数传入或作为返回值返回。 (0:00) 装饰器在底层完全等价于语法糖 `func = dec(func)`,其核心是接收一个可调用对象并返回一个被重新赋值给原函数名的新对象。 (5:00) 带参数的装饰器结构并未引入新魔法,仅是通过三层嵌套函数增加了一次前置调用,等价逻辑展开为 `func = dec(arg)(func)`。 (10:00)

Reading summary

码农高天指出,Python 装饰器没有任何神秘底层机制,其本质是建立在“函数是一等公民对象”基础上的语法糖,核心等价于将函数作为输入并接收返回新对象的赋值操作。 前置基础:函数即对象与高阶调用 (0:00) 理解装饰器的前提是认识到 Python 中“一切皆对象”,函数也不例外。在字节码层面,定义函数仅是将函数体编译为代码对象(code object),随后经由 MAKE_FUNCTION 指令构造成函数对象(function object),并最终存储在对应的变量名中。函数对象具备 callable 特征,即可以通过在名称后附加小括号发起执行,而对非 callable 对象(如数字)尝试调用虽可通过编译,但会在运行时引发错误。 由于函数只是普通变量,因而可以自由传递。一方面,函数能作为参数传给另一个高阶函数(如在 calc_number 中接收 double 或 triple 并传入数值执行计算);另一方面,函数也能作为返回值被返回,例如在 get_multiple_func 内部动态生成并返回一个倍数函数。这些特性构成了装饰器语法存在的技术基石。 装饰器本质:等价替换与通用包装器 (5:00) 在字节码层面,在目标函数上方标注 @dec 的操作,底层逻辑是先构造原函数,随后调用装饰器函数,并将返回结果重新存回原变量名。码农高天强调,装饰器完全等价于 double = dec(double)。为了消除对装饰器的敬畏感,他举出极端反例:若装饰器 dec(f) 内部直接返回数字 1,则被装饰后的变量打印出来就是一个纯粹的整数 1,证明其在语法层面上仅是直接的重命名赋值。 在常规工程开发中,装饰器通常用于扩展函数功能。以性能计时器 timing 为例,装饰器接收被修饰的函数 f,并在内部构建包装函数 wrapper。包装器在调用原函数前后记录时间戳并打印耗时,最后返回原函数的结果。通过在 wrapper 中使用 *args 和 **kwargs,装饰器能够兼容任意签名与参数数量的业务函数,实现无侵入的功能复用。 进阶实现:带参数装饰器的三层拆解 (10:00) 带参数的装饰器(如 @timing(1000))常常引起理解困惑,但码农高天指出,它相较于无参数装饰器仅仅是在等价转换中多了一次普通函数调用,即形式从 double = timing(double) 变为 double = timing(1000)(double)。因此,该装饰器必须返回一个可接收目标函数的高阶函数。 具体实现结构可清晰拆分为三层嵌套:最外层函数(如 timing(iterations))专门接收用户配置参数,并返回中间层函数 inner;中间层函数充当经典的无参装饰器,接收待装饰的目标函数 f 并返回包装器 wrapper;最内层的 wrapper(*args, **kwargs) 则负责接管实际执行逻辑,例如在内部按配置循环运行 iterations 次目标函数,完成耗时统计并返回原函数的执行结果。 无论是基础包装还是带参数的多层嵌套,装饰器均可还原为 Python 原生的对象赋值与多级函数调用链条。只要牢记核心等价公式与调用层级,即可消除对各类装饰器语法的混淆。

Source quotes

[5:00] 码农高天: 当我们对一个函数做一个decorator的操作的时候它完全等价于这个变量名等于Decorator调用这个变量名也就是说Decorator不过是一个输入和输出都是函数的函数 [0:00] 码农高天: 当我们在python中所谓定义一个函数的时候我们只是新建了一个变量然后这个变量里面保存了一个函数对象也就是function object

Original transcript

[0:00] 这期视频我们来讲Decorator 也就是装饰器很多刚学python的人会对这个概念感到非常迷茫因为很多其他的语言并没有这样一个语法然后你去网上找例子跟教程他的例子往往也让人望而却步对吧就像这样哪怕最简单的例子看起来都很复杂今天我就把Decorate这件事情给掰开了揉碎了好好的讲明白它到底是什么以后你再看到它就再也不会迷茫了在讲decurry之前我们要先理清楚python的一些基础知识第一件事就是函数是什么那可能有些人知道在python中的所有东西都是对象都是object函数也不例外 函数只是python中的一个普通的object而已那我们来看一下左边这几行代码编译之后的字节码它没有什么神秘的函数体的部分被编译成一个code object这里面保存代码运行的逻辑然后我们看上面这个部分它就是把这个code object 露了进来然后把这个名字double露了进来然后做了一个function objectmake function就是把code object变成一个有名字的function object然后把这个function object保存到了double这个名字里所以说当我们在python中所谓定义一个函数的时候我们只是新建了一个变量然后这个变量里面保存了一个函数对象也就是function object我们可以看这个double本身啊它就是一个object那么函数对象有一个特点就是它是一个callable在python里面 callable就是这个东西你后面可以跟一个小括号然后去调用它那我们定义的这个函数呢他需要一个参数X所以在我们每次调用的时候呢我们还需要给他一个参数实际上在python的语法层面 任何的对象你都可以调用但是这个东西如果不是callable的话它在run time会出错我们看 我们把这个一括号进行编译 它也是可以编译通过的只不过他会给你一个Syntax warning告诉你这个好像是错的但是他的字节码表示一样可以load完这个1之后然后再尝试call他那当你理解了在python中函数不过是一个普通的对象的时候你就非常容易理解函数也可以被当作参数传进其他的函数里比如在这里我们定义了一个double定义了一个 triple这两个保存着函数对象的变量都可以被传进Calc number这个函数里Calc number这个函数 第一个参数是一个函数第二个参数是一个数然后他就把函数对这个树进行一个调用当传进double3的时候呢他就会打印一个double3当传进triple3的时候 他就会打印一个triple3我们运行一下请不吝点赞 订阅 转发 打赏支持明镜与点点栏目大家可以看 第一个打印出来的是3*26第二个答应的是3乘以39所以函数并没有什么神奇的地方它完全可以像其他的变量一样被传来传去被传进其他的函数同样的 函数不光可以被作为变量传进其他的函数 函数本身也可以成为一个返回值比如我们左边这个getMultipleFunk它本身是一个函数它返回的也是一个函数而他返回的这个函数做什么事情和getMultipleFunk得到的参数有关如果你给getMultipleFunk的参数是2他就返回一个函数这个函数把输入值乘2返回这块稍微有点绕啊 一定要听明白get multiple func take argument是n它返回的这个函数是multiplemultiple也是一个函数而multiple返回什么值是由getmultiplefunk的参数N决定的它返回的是n乘x因此getMultipleFunk2返回的就是double这个函数getMultifunk3返回的就是triple这一个函数而这两个函数都是可被调用的也就是都是callable我们可以看到下面我们就在正常的使用这两个函数跟之前我们define的那个double跟triple一样对吧 double 括号三 triple 括号三我们运行一下我们可以看依然是输出的6和9好 如果你能理解函数可以作为一个参数被传进其他函数你也可以理解函数的返回值可以是一个函数那么decorator相对来说就变得非常容易理解了好 那我们现在写了一个极简的decorator首先你要知道的第一件事就是decorator本身就是一个callable他也没有特殊的地方那在最开始的时候呢 [5:00] 理解为decorator本身就是一个函数也就是at后面跟的这个名字就是一个函数名我们可以看到我们定义了一个函数叫做deck然后我们把这个double上面加了一个atdeck接著我们来看右边的字节码上半部分没有任何特殊的地方就跟我们刚才定义一个函数是长得一模一样的那这个部分就是Decorator真正起作用的部分我们可以看到中间和刚才定义那个函数依然是一样的中间定义的是一个double函数而真正的decorator起效果的是call function跟store name和前面的load name decorator一起看我们就知道Decorate本身只是一个语法堂它和下面这个写法是一模一样的当我们对一个函数做一个decorator的操作的时候它完全等价于这个变量名等于Decorator调用这个变量名也就是说Decorator不过是一个输入和输出都是函数的函数当然了 你说输入输出都一定是函数吗输入一定是 输出还真不一定是为了帮大家理解我们举这么一个极端的例子我们看左边的代码没有人这么写过代码吧没有人decorate return过一个1对不对我们的decorator return是一个1然后我们把这个double上面加上一个decorator然后printdouble我们看看结果是什么很简单就是1啊注意我们print是double这个变量本身而不是对double进行任何的调用不是什么double 扩号2 扩号3之类的为什么double变成1了呢 很简单啊我们说这个部分和它是等价的对不对那如果deck返回的是1的话那么deckdouble返回的是11被复制给了doubleprint1就是1啊那这个例子也是希望告诉大家Decorate这个东西本身没有任何的神秘性他就是一个语法堂而已好 那当然了 在绝大部分的情况下 我们使用decorator的时候 还是认为decorator是一个参数是函数 返回值也是函数的这么一个函数好 让我们回到刚刚开始那个例子那为了减少大家的发蒙程度呢我把args跟keyword args先拿掉了我们可以看到timing是一个函数对吧我们把它当作decorator来用他take的这个f是一个函数他返回rapper还是一个函数那wrapper这个函数能干嘛呢rapper这个函数首先和myfunk一样都take一个argument然后他刚开始记一个十然后调用time at 给进来的这个参数 f记录返回值打印函数用实然后返回返回值 好 那我们看一下这个decorate加上之后有什么效果可以看到它就是起到了一个计时并且把这个函数运行时间打印出来这么一个功能使用decorated的好处就是当你有不同的函数的时候你都可以比较简洁的来完成这件事比如说我们还有一个函数我们有一个other funk对吧这个函数返回一个x乘以2我们把它加上一个decorate之后呢就会有这个效果他就告诉你运行这个函数花这么长时间然后返回值是在这21行被打印的好 那我们回到它的初始形态 为什么这块用星args星星keywordargs呢这两个函数参数大家可以去自己查一下本质上他们就是允许变长的函数参数这样的话不管我装饰的是什么函数他都能用刚才那个decorator只能用在只有一个参数的函数上现在比如说这个函数可能有若干个参数像这样这样也是可以用的好 那我们再进阶一下有人说了我还见过带参数的decorator那是怎么回事呢其实带参数的decorator也没有任何神奇的地方它就是相当于等价的过程中多了一次函书调用原来不带参数的时候是直接time at 括号 double直接用timeit这个函数去调用double对不对现在带了参数他要先计算timeit括号10也就是这个参数这个东西返回的是一个函数这个函数再去调用double再返回一个函数他们两个现在变成完全等价的原来这个Decorator是参数是函数返回值也是函数现在这个decorator是参数是可以你任意指定的东西然后他要返回一个函数A 这个函数A的参数是函数 返回值也是函数现在的 [10:00] 要返回一个原来的那种decorator好 我们看一下实现啊最外层的我们使用的decoratortake的是一个argument参数是运行多少次而它返回的是一个函数 这个函数叫做innerinner是我们之前那个版本没有参数的decorator的那个decorator我们可以看到这个inner跟刚才一样pick是一个函数返回的也是一个函数而这个函数rapper里面是运行了Iteration次的函数F那本质上它相当运行了这么一个东西啊首先time at 1000拿到了一个inner这个函数然后inner对double做一个调用返回了一个函数保存到double里我们看一下运行结果可以看到这是跑1000次double所需要的时间好 今天详细的介绍一下decorator用法和它背后的原理希望大家可以在弹幕里或者评论区告诉我你是不是喜欢这样的视频这期视频就到这里如果你喜欢的话记得点一下关注别忘了一键三雷优优独播剧场——YoYo Television Series Exclusive