高天破解Python类内装饰器冲突 以后置staticmethod打通调用

【python】如何在class内部定义一个装饰器?这里的坑你要么不知道,要么不会填! — 码农高天

码农高天

Python在类定义阶段会将类体作为局部作用域运行,提前使用@staticmethod语法糖会将其包装为不可直接调用的描述符,因此必须在类定义末尾显式执行函数赋值才能兼顾类内装饰与外部调用。

AI-assisted summary of 【python】如何在class内部定义一个装饰器?这里的坑你要么不知道,要么不会填! — original source. Check the original for full context.

Source published:

Key ideas

  • 将通用装饰器封装到类中有助于代码组织,但若定义为普通实例方法或类方法,会导致外部使用时必须多余实例化或在签名中残留无用的 self / cls 参数。 (0:00)
  • 使用 @staticmethod 封装装饰器仅适用于类外调用;若尝试在同一个类体内部直接用它修饰同类方法,会因描述符包装导致类加载期调用报错。 (5:00)
  • 终极解决方案是利用 @staticmethod 的等价赋值语法:在类体顶部裸写纯函数供内部修饰,在类体末尾追加 `func = staticmethod(func)` 导出为静态方法,兼顾类内外双向兼容。 (7:28)

Detailed notes

## Background 该视频由 YouTube 频道“码农高天”于 2024 年 4 月 17 日发布。视频主要探讨如何在 Python 类的内部定义和使用装饰器,并深入剖析背后诸多与封装和命名空间相关的非直观知识与避坑指南。

## TL;DR 视频从一个简单的日志装饰器切入,逐步演示了将其封装进类中的演进过程:从笨重的普通实例方法到残留无用参数的 `@classmethod`,再到适用于纯外部调用的 `@staticmethod`。随后,高天重点剖析了“用类内装饰器修饰同类方法”时直接加静态方法会报错的深坑,解释了类定义体在加载期作为局部作用域执行的底层原理。最终,视频给出了兼顾类内直接修饰与类外静态调用的终极方案——在类体末尾使用 `log_function = staticmethod(log_function)` 等价语法进行后置转换。

## Atmosphere & style 视频展现出一种硬核、严谨而又抽丝剥茧的技术极客风格。高天循序渐进地抛出反直觉的报错场景与代码坏味道,再带领观众深入 Python 解释器的执行模型探寻本质原因。观看体验如同解开一层层精巧的底层机制谜题,从最初对语法特性的困惑自然过渡到豁然开朗的透彻领悟。

## Bulletin - [00:00](https://www.youtube.com/watch?v=srSA-aiHf8Y&t=0s) 将通用装饰器封装到类中有助于代码组织,但若定义为普通实例方法或类方法,会导致外部使用时必须多余实例化或在签名中残留无用的 self / cls 参数。 - [05:00](https://www.youtube.com/watch?v=srSA-aiHf8Y&t=300s) 使用 @staticmethod 封装装饰器仅适用于类外调用;若尝试在同一个类体内部直接用它修饰同类方法,会因描述符包装导致类加载期调用报错。 - [07:28](https://www.youtube.com/watch?v=srSA-aiHf8Y&t=448s) 终极解决方案是利用 @staticmethod 的等价赋值语法:在类体顶部裸写纯函数供内部修饰,在类体末尾追加 `func = staticmethod(func)` 导出为静态方法,兼顾类内外双向兼容。

## Key takeaways - [00:00](https://www.youtube.com/watch?v=srSA-aiHf8Y&t=0s) 随着项目膨胀,将功能相关或高度内聚的装饰器归拢到独立的工具类中是常见的工程设计需求。 - [00:45](https://www.youtube.com/watch?v=srSA-aiHf8Y&t=45s) 将装饰器定义为常规实例方法(带 self 参数)会导致调用链冗余,迫使外部必须先实例化对象(如 @d.log_function)才能使用。 - [01:30](https://www.youtube.com/watch?v=srSA-aiHf8Y&t=90s) 若装饰器本身并不依赖类或实例的内部状态,在方法签名中保留 self 参数属于无意义的接口污染。 - [02:15](https://www.youtube.com/watch?v=srSA-aiHf8Y&t=135s) 改用 @classmethod 可以免除实例化步骤直接通过类名调用,但函数签名中依然残留无法消除的 cls 参数。 - [03:00](https://www.youtube.com/watch?v=srSA-aiHf8Y&t=180s) 对于单纯起命名空间归类作用的外部装饰器,定义为 @staticmethod 并彻底移除 self / cls 是最干净的标准做法。 - [05:00](https://www.youtube.com/watch?v=srSA-aiHf8Y&t=300s) 当尝试在同一个类体内部用该装饰器装饰其他同类方法时,保留 @staticmethod 会在类定义阶段触发异常。 - [06:00](https://www.youtube.com/watch?v=srSA-aiHf8Y&t=360s) 在类定义执行期间,类体本质上是一个局部的代码块作用域(local namespace),尚未形成完整的类对象。 - [06:35](https://www.youtube.com/watch?v=srSA-aiHf8Y&t=395s) 在类体中不加任何装饰器、不带 self 的裸函数,在类定义阶段可作为普通局部可调用对象直接修饰其他方法。 - [07:05](https://www.youtube.com/watch?v=srSA-aiHf8Y&t=425s) 完全裸写的类内函数无法妥善支持外部访问,类构建完成后通过实例访问会触发方法绑定并因缺失 self 参数报错。 - [08:00](https://www.youtube.com/watch?v=srSA-aiHf8Y&t=480s) 装饰器语法糖本质上是对函数对象的就地重新赋值(即 func = decorator(func))。 - [08:25](https://www.youtube.com/watch?v=srSA-aiHf8Y&t=505s) 前置使用 @staticmethod 语法糖会立即使其变成静态方法描述符对象,破坏其在类定义作用域内的可调用性。 - [07:28](https://www.youtube.com/watch?v=srSA-aiHf8Y&t=448s) 后置显式调用 `log_function = staticmethod(log_function)` 能够在类定义期保留裸函数特性,在类导出时转换为合法的静态方法。 - [09:00](https://www.youtube.com/watch?v=srSA-aiHf8Y&t=540s) 理解这一技巧的关键在于掌握 Python 类定义的加载执行模型,以及函数转变为方法时的属性描述符绑定机制。

## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=srSA-aiHf8Y&t=0s) 为什么要把装饰器封装到类中?从实例方法到类方法

视频开篇以一个基础的日志装饰器 `log_function` 切入。这个装饰器的逻辑非常直观:在目标函数执行前先打印函数开始运行及其入参,在目标函数执行完毕后再打印函数结束。高天在下方定义了一个简单的斐波那契数列函数来演示效果,终端输出表明装饰器能够正常拦截并打印递归调用的每一层细节。通常情况下,开发者习惯直接把装饰器写在模块全局命名空间的最外层,但随着项目规模扩大,并不是所有工具函数都适合丢在全局。当开发者有多个功能相近的装饰器,或者出于高内聚的抽象设计理念,便会萌生将装饰器收拢并封装到一个独立的类(如 `Decorators` 类)中的想法。然而,对于习惯了普通函数装饰器的人来说,这一步往往会踩入诸多不直观的深坑。

最容易想到的实现方式是把装饰器当成普通类方法,为它硬加上一个 `self` 参数后直接塞进类体。但这种做法立刻带来了调用链上的尴尬:由于普通类方法必须由实例调用,若想在外部装饰一个函数,就不得不先实例化一个对象,写出 `d = Decorators()`,然后再通过 `@d.log_function` 来修饰函数。高天指出这种方式的体验非常差:“首先非常明显的,我们为了使用装饰器,迫不得已的要建立一个新的对象,这个事情非常不爽;其次我这个装饰器里面莫名其妙出现了一个 self argument,然而我这个装饰器压根就没有用到它。”

为了摆脱“必须先实例化”的束缚,下一步改进是引入 `@classmethod` 装饰器。将方法标记为类方法后,其第一个参数变为 `cls`,外部调用时便可以直接使用 `@Decorators.log_function`,省去了建立实例对象的冗余步骤。但这仍未彻底解决代码洁癖的问题——只要装饰器本身的功能不依赖该类或对象的内部属性,那个强行挂在函数签名第一位的 `cls` 参数就依然显得格格不入且毫无用处。

```mermaid flowchart TD accTitle: 类中定义装饰器的演进路径 accDescr: 展示从普通实例方法到类方法再到静态方法的逐步演进过程

subgraph S1["初步方案:普通实例方法"] A["定义 def log_function(self, func)"] --> B["必须先实例化:d = Decorators()"] B --> C["使用 @d.log_function 装饰"] C -.-> D["痛点:产生多余对象与无用 self 参数"] end

subgraph S2["改进方案:类方法"] E["标记 @classmethod<br/>def log_function(cls, func)"] --> F["直接通过类调用:@Decorators.log_function"] F -.-> G["痛点:签名仍残留无用的 cls 参数"] end ```

---

[03:00](https://www.youtube.com/watch?v=srSA-aiHf8Y&t=180s) 使用 @staticmethod 实现无副作用的类装饰器封装

当然,在某些特定业务场景中,装饰器确实需要读取类成员甚至实例成员的动态状态,从而根据类或对象的变化来改变自身的拦截行为,此时保留对象方法或类方法的形态是合理的。但在绝大多数纯粹的抽象与封装需求中,装饰器仅仅是挂靠在该命名空间下的通用工具,与宿主类的具体属性毫无羁绊。

针对这种纯粹的封装诉求,最佳手段是将装饰器转变为静态方法(`@staticmethod`)。高天演示了具体修改:直接将 `log_function` 签名中多余的 `self` 或 `cls` 参数彻底删掉,仅保留真正的目标函数参数 `func`,并在函数定义上方标注 `@staticmethod`。这样一来,`log_function` 成为挂在 `Decorators` 类下的静态成员,既没有改变原先纯函数的内部签名与逻辑,又通过类的命名空间完成了归类组织。在外部使用时,开发者依然只需书写 `@Decorators.log_function` 即可顺畅调用。当仅仅需要把装饰器封装到某个类中供外部使用时,这无疑是一种干净、标准且利落的处理模式。

---

[05:00](https://www.youtube.com/watch?v=srSA-aiHf8Y&t=300s) 类内互调的暗坑:为什么给装饰器加 @staticmethod 会报错?

紧接着,高天抛出了一个在实际项目中极为高频且无数人最终铩羽而归的难题:“如果我想用我类里面定义的属于这个类的装饰器,去装饰我这个类里面的方法,我应该怎么做?”比如,现在直接把待修饰的 `fib` 斐波那契函数也挪到 `Decorators` 类中,希望用同类里定义的 `log_function` 去装饰它。

许多人按照直觉保留刚才的 `@staticmethod` 并在同类方法上直接标注 `@log_function`,结果 Python 解释器在类加载阶段就会直接抛出报错;若尝试改成普通的实例方法或类方法,也同样无法正常生效。高天揭晓了一个让许多初学者大为震惊的反直觉答案:在这种场景下,正确的做法是直接把 `log_function` 原封不动、不带任何装饰器、也不带任何 `self` 参数地裸写在类体中。

看到类里面定义了一个既没有 `@staticmethod` 也没有 `self` 的函数,很多程序员的第一反应是觉得这不合规范,但运行后却发现它能完美运行。高天点明了背后的 Python 语言底层原理:“我们在之前的视频里面提到过,当我们在定义一个新的 class 的时候,它本质上是把 class 里面的 code block 在另外一个命名空间运行了一下。”在类定义的执行期,类体实际上就是一个局部的局部作用域(local namespace);只要在上面定义了普通的函数名字 `log_function`,紧随其后的方法就可以直接将它作为作用域内的普通可调用对象(callable)使用。它在定义阶段根本不是面向对象意义上的“method”,而是一个纯粹的临时辅助函数。

然而,这种裸写方式存在致命的外部兼容性缺陷。当类定义完成之后,由于它没有被声明为静态方法,若在类外部尝试通过实例 `d.log_function` 去访问它,Python 会试图将其绑定为普通实例方法并传递 `self`,从而引发参数不匹配的报错。

```mermaid flowchart LR accTitle: 类定义体执行原理与静态方法报错机制 accDescr: 说明类定义期间代码块作为局部命名空间执行的特性

subgraph ClassScope["类定义体执行阶段(局部命名空间)"] direction TB N1["执行代码块:创建裸函数 log_function"] N2["紧随其后的方法执行 @log_function(fib)"] N1 -->|"作为普通可调用对象"| N2 end

subgraph MethodDescriptor["外部访问阶段(属性描述符触发)"] direction TB E1["外部实例访问 d.log_function"] E2["触发函数描述符,尝试自动注入 self"] E1 --> E2 E2 -->|"缺少 self 入参"| E3["抛出 TypeError 报错"] end

ClassScope --> MethodDescriptor ```

---

[07:28](https://www.youtube.com/watch?v=srSA-aiHf8Y&t=448s) 终极解法:利用装饰器等价语法实现内外通用的静态装饰器

面对既要在类内部直接修饰同类方法,又要在类外部支持通过类名(`Decorators.log_function`)或实例(`d.log_function`)任意调用的终极需求,高天给出了最优雅的终极解法——手动利用静态方法的等价赋值语法。

具体做法是在定义 `log_function(func)` 时先不要添加 `@staticmethod` 语法糖,使其保持纯函数状态供类体下方的同类方法(如 `fib`)直接作为装饰器使用;而在整个类体定义的最后一行,显式补上一行赋值语句: ```python log_function = staticmethod(log_function) ```

高天解释了这行代码的本质机制:在 Python 中,`@staticmethod` 装饰器语法本质上就是 `func = staticmethod(func)` 的语法糖。如果在函数定义处直接加 `@staticmethod`,该函数在类定义体中立刻被包装为静态方法描述符对象,导致其在接下来的类作用域内无法像普通函数那样直接被当作装饰器调用;“只不过用装饰器的语法,你只能在定义它的同时装饰它。而当我们使用它的等价语法的时候,我们可以先定义它成一个正常的函数,然后在 class 的 definition 里面把它当成一个正常的装饰器去使用,最后再通过 staticmethod 把这个 log_function 变成一个 static method。”

加上这行后置处理代码后,类内方法成功吃到了裸函数的装饰效果,而在类定义完成后,该函数又被正式转换为静态方法,无论是通过 `Decorators.log_function` 还是 `d.log_function` 访问都能完美符合 Python 的描述符语义。高天最后总结道,这些看似神奇的奇技淫巧,其背后全都是 Python 基础执行模型与装饰器本质概念的自然推论。

- 关键机制总结: - 类体在加载时是作为独立的局部作用域执行的,未加工的裸函数在该作用域内具有直接可见性与可调用性。 - `@` 语法糖是就地立即求值的,提前将函数变为 `staticmethod` 对象会破坏其在类作用域内的直接可调用状态。 - 后置显式调用 `log_function = staticmethod(log_function)` 完美兼顾了“类内直接当装饰器使用”与“类外作为静态方法导出”的双重需求。

## Notable quotes > [05:00](https://www.youtube.com/watch?v=srSA-aiHf8Y&t=300s) 当我们在定义一个新的 class 的时候,它本质上是把 class 里面的 code block 在另外一个命名空间运行了一下。 — 码农高天

## Mentioned Python, 码农高天, Decorators, log_function, fib, staticmethod, classmethod

## Video description 这期视频讲一下如何在类的内部定义装饰器,尤其是如何在类的内部使用这个装饰器。这里有很多和封装有关的并不直观的知识,看了才知道!

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

中文简报

高天破解Python类内装饰器冲突 以后置staticmethod打通调用 Python在类定义阶段会将类体作为局部作用域运行,提前使用@staticmethod语法糖会将其包装为不可直接调用的描述符,因此必须在类定义末尾显式执行函数赋值才能兼顾类内装饰与外部调用。 视频从一个简单的日志装饰器切入,逐步演示了将其封装进类中的演进过程:从笨重的普通实例方法到残留无用参数的 `@classmethod`,再到适用于纯外部调用的 `@staticmethod`。随后,高天重点剖析了“用类内装饰器修饰同类方法”时直接加静态方法会报错的深坑,解释了类定义体在加载期作为局部作用域执行的底层原理。最终,视频给出了兼顾类内直接修饰与类外静态调用的终极方案——在类体末尾使用 `log_function = staticmethod(log_function)` 等价语法进行后置转换。 将通用装饰器封装到类中有助于代码组织,但若定义为普通实例方法或类方法,会导致外部使用时必须多余实例化或在签名中残留无用的 self / cls 参数。 (0:00) 使用 @staticmethod 封装装饰器仅适用于类外调用;若尝试在同一个类体内部直接用它修饰同类方法,会因描述符包装导致类加载期调用报错。 (5:00) 终极解决方案是利用 @staticmethod 的等价赋值语法:在类体顶部裸写纯函数供内部修饰,在类体末尾追加 `func = staticmethod(func)` 导出为静态方法,兼顾类内外双向兼容。 (7:28)

Reading summary

码农高天指出,在Python类中封装装饰器时,常规的实例方法、类方法或直接使用`@staticmethod`均会在外部调用或类内互调场景下引发缺陷;理解类定义的局部命名空间本质,并通过后置显式转换静态方法,才能实现内外兼顾的终极封装。 从实例方法到类方法的封装痛点 (0:00) 当开发者希望出于高内聚的抽象设计将多个装饰器(如日志拦截器)收拢至独立类时,最直接的做法是作为类成员定义。然而,若将其写为普通实例方法并强加`self`参数,外部在装饰函数时便被迫先实例化对象(如`d = Decorators()`再`@d.log_function`),不仅产生冗余对象,且函数体内完全不使用该`self`参数。 改用`@classmethod`修饰装饰器后,调用端虽然可以直接通过类名访问(`@Decorators.log_function`),摆脱了实例化的限制,但函数签名第一位依然必须残留无实际业务作用的`cls`参数。只要装饰器本身不需要读取类或实例的内部动态状态,这种强制挂靠的签名就存在代码冗余与语义不匹配的问题。 纯静态封装与类内互调的冲突 (3:00) 对于仅做命名空间归类的纯粹封装,去除`self`或`cls`并标记为`@staticmethod`能够使外部调用保持干净。但在实际业务中,若试图在定义装饰器的同一个类中,直接用该装饰器修饰同类下的其他方法,保留`@staticmethod`会导致Python解释器在类加载阶段直接抛出报错。 高天指出,反直觉的临时解决方式是在类体中“裸写”该装饰器函数——既不挂`@staticmethod`也不加`self`。因为Python在定义类时本质上是将类体代码块放在独立的局部命名空间内执行,未加工的裸函数在该作用域内只是普通的局部可调用对象,类内后续方法可以直接调用。但这种写法的缺陷在于类定义完成后,若外部通过实例访问,Python会尝试将其绑定为普通实例方法并自动注入`self`,最终因入参不匹配抛出`TypeError`。 利用等价语法实现内外通用的终极方案 (7:28) 要同时满足“类内部直接修饰同类方法”与“外部通过类或实例顺畅调用”,关键在于避开`@`语法糖的立即求值机制。`@staticmethod`在语法糖生效时会立刻将函数包装为静态方法描述符对象,破坏其在类定义执行期间作为纯函数的直接可调用性。 高天给出的标准解法是利用静态方法的等价赋值语句:在类体中先以纯函数形式定义装饰器,供类内后续方法直接以`@log_function`的形式调用;随后在整个类定义的末尾显式执行`log_function = staticmethod(log_function)`。这一后置处理使装饰器在类加载期充当临时辅助函数,而在类定义完成后正式转变为标准的静态方法描述符,完美兼容类名与实例的访问需求。 该方案展示了Python底层执行模型对语法特性的制约:理解类体作为局部执行作用域的本质以及装饰器语法糖的展开过程,可以避免生搬硬套面向对象规则,用极简的语法特性解决复杂的架构封装冲突。

Source quotes

[5:00] 码农高天: 当我们在定义一个新的 class 的时候,它本质上是把 class 里面的 code block 在另外一个命名空间运行了一下。

Original transcript

[0:00] 这期视频呢我们来讲一个跟装饰器也就是decorator有关的用法我们看这里我们写了一个decorator log function这个decorator很简单 就是在运行一个函数之前 先打印一下函数开始了 再打印一下这个函数的argument在运行函数之后 打一个函数结束了那我们在下面写了一个简单的费波纳切函数我们把这个费波纳切函数加上我们的装饰器看一下结果可以看到虽然输出用处似乎不是那么大但是他确实是工作了的如果你细细的分析的话也是能感受到里面帮助你理解地规的一些信息啊然而装饰器的功能并不是我们今天的重点我们在写装饰器的时候经常会把装饰器函数放到一个文件的全局的秘密空间里也就是把这个函数定义在这个文件的最外层然而我们知道并不是所有的函数都适合放到全局空间的那有的时候我就是想把我的装饰器放到一个类里面我希望把它抽象或者说我有若干个功能相似的装饰器我就是希望把它们归类到同一类里面那到这里的时候很多人就不知道该怎么办了啊所以我们这期视频的重点是讲如何把一个装饰器放到类里面好 我们来看我们左边的程序 这是我们最容易想到的一种实现把装饰器挪到类里面本质上它可能就相当于把一个函数变成类的方法对不对我们就按照一般的方式把函数加上一个self argument然后直接把它扔到类里面就可以了那当你使用这种方法的时候你首先要意识到一个类的方法必须由这个类的对象所调用所以当我们想使用这个装饰器的时候,我们首先要建立一个Decorators这个class的object。也就是第11行这里的D=decorators那接下来我们就可以用这个d.log function来装饰这个斐波纳契函数我们可以看到这种使用方式最后得到的结果跟之前是一样的那这么用有什么问题呢啊 它确实是有首先非常明显的 我们为了使用装饰器 迫不得已的要建立一个新的对象 这个事情非常不爽其次我这个装饰器里面莫名其妙出现了一个self argument 然而我这个装饰器压根就没有用到它 对吧好那解决第一个问题我们知道有一种方法可以把一个类里面定义的method变成class method也就是把这个方法从一个对象才能调用的方法变成一个类就可以调用的方法这里我们可以使用这个class method装饰器我们看在使用了class method装饰器之后这个lock function就变成了一个类方法那在第12行的时候我们就不需要再新建一个对象才能使用这个装饰器了我们可以直接用这个decorator'slay.log function来调用这个装饰器那现在的状态呢确实是比之前要稍微好一些但是这个class method它第一个argument依然是class我们还是没有用到变量对吧当然了在某些情况下我们可能会想使用到这个class里面的一些member甚至是一个object里面的一些member也就是我这个装饰器啊会根据我这个class或者这个object的变化改变他自己的功能那这种情况下我们当然可以把它作为一个对象方法或者把它作为一个类方法可如果我们就想用现在的这个装饰器它确实是跟对象无关跟类也无关的时候我们单纯的只是想把它放到类里面做一个封装我们就可以把这个class method变成一个static method我们看我们可以把这个log function里面的第一个argument刚才的self或者class直接删掉然后使用这个static method去装饰它这样这个lock function就变成了一个静态方法它依然是被封装在这个类里的但是它的功能与这个类其他的东西是无关的在使用的时候我们依然是用这个Decorators.log function但是我们看log function的definition已经跟刚才我们写的一模一样了那当你仅仅需要把一个装饰器封装到一个类里面的时候这就是一个非常不错的方式 [5:00] 然而这还没完接下来我们要讲一个很多人都遇到过然后放弃了的问题就是如果我想用我类里面定义的属于这个类的装饰器去装饰我这个类里面的方法我也应该这么做我们看我把这个fib函数给挪到了这个decorators类里这其实是我们非常常见的一种可能性就是我一个类里面的很多方法我都希望用装饰器帮它装饰一下但是我这个装饰器只会在这个类里面用 我又想把这个装饰器给封装到这个类里来很多人这时候就不知道该怎么办了如果像我们刚才讲的使用static method你会发现他直接给你报错了有的人可能说我直接把它当成一个正常的类的method呢当然依然是不对的那在这里告诉大家这种情况下正确的用法是直接把log function这个函数原封不动的放到这个decorators class里不需要给他加任何的装饰器好多人看到这可能就蒙了对吧说这是什么东西我从来没有在python里见到过长成这样的累的方法然而如果你运行一下这段程序的话你会发现他是正常工作的当然由于我们修改了费这个函数所以我们在打印2个时候呢他多了一个东西但是我们可以看到log function这个decorator他是正常工作的那很多人可能觉得莫名其妙对吧为什么呢原因其实也很简单我们在之前的视频里面提到过当我们在定义一个新的class的时候它本质上是把class里面的code block在另外一个命名空间运行了一下而我们只要把这个codeblock单独拿出来他为什么工作所有人都知道只不过我们非常不习惯在class里面看到这种没有self的method那实际上这个log function它也不是一个method你可以认为它是在我们定义这个类的时候里面的一个辅助函数或者是一个辅助的decorator什么意思呢?当我们尝试在这个类的定义之外,使用d.log function的时候他会报错因为他会尝试把这个log function当成一个正常的对象方法去解读当然如果你直接使用Decorator style log function它是可以工作的因为这个decorators.log function就是一个正常的函数好那最终的问题出现了有没有一种方式可以把我这个装饰器封装到泪里在这个类里可以正常使用而在这个类之外无论是通过对象调用还是通过类调用都可以正常使用呢答案是有的你只需要在类定义的最后加上这行代码如果你看到过我之前讲装饰器的视频的话你就会意识到这行代码的本质和在log function的定义上加一个atStaticMethod这个装饰器是一样的他们两个是等价的只不过用装饰器的语法,你只能在定义它的同时装饰它。而当我们使用它的等价语法的时候我们可以先定义它成一个正常的函数然后在class的definition里面把它当成一个正常的装饰器去使用最后再通过static method把这个log function变成一个static method我们看在增加了这行神奇的代码之后无论我们是使用decorators.log function还是使用它的对象d.log function都能正确的执行这个装饰器的功能了那实际上这个所谓技巧的全部原理我在之前的视频中其实都提到了只不过大家对这些原理的掌握可能还不足够熟悉啊所以闲来无事还是可以翻翻我之前的视频啊好 那这期视频就到这里 希望大家有所帮助 如果喜欢的话记得点下关注 别忘了一键三雷哦优优独播剧场——YoYo Television Series Exclusive