CPython借描述器协议解析类属性 确保classmethod绑定类型

【python】staticmethod与classmethod深度机制解析——要知其所以然 — 码农高天

码农高天

类属性查找通过底层type_getattro无条件触发描述器协议,使staticmethod实时解包为裸函数,并确保classmethod在任何调用方式下均自动绑定类对象作为首参数。

AI-assisted summary of 【python】staticmethod与classmethod深度机制解析——要知其所以然 — original source. Check the original for full context.

Source published:

Key ideas

  • staticmethod 和 classmethod 均为内置装饰器,但传参机制截然不同:静态方法不隐式注入任何首参数,而类方法无论通过类还是实例调用,首个实参始终自动绑定为当前类对象。 (0:00)
  • 类字典 A.__dict__['f'] 中实际存储的是 staticmethod 或 classmethod 实例,直接通过点号访问时能拿到函数或绑定方法,完全依赖于底层描述器协议(Descriptor Protocol)。 (5:00)
  • 类属性查找遵循专门的 type_getattro 机制,只要查找到的对象定义了 tp_descr_get,无论其是否为数据描述器,都会无条件调用其 __get__ 方法进行解析。 (5:00)
  • classmethod 的描述器在被触发时,会提取当前类或实例所属的 type,并将其与原函数包装成首参数固定的 bound method 返回。 (10:00)

Detailed notes

## Background 该视频由“码农高天”于2024年2月9日发布在YouTube平台上。视频简介提到,本期内容深入探讨Python中的`staticmethod`和`classmethod`及其背后的实现机制,挖掘开发者平时容易忽略的底层细节。

## TL;DR 视频首先对比了`staticmethod`与`classmethod`在表层用法上的异同,强调静态方法本质上是置于类命名空间下的普通函数,而类方法则无论通过类还是实例调用都会自动隐式传入当前类型对象。随后,作者深入探究了底层实现,指出类属性字典中实际保存的是内置的装饰器包装对象。通过结合CPython源码与描述器协议(Descriptor Protocol),视频详细剖析了`type_getattro`查找属性时触发`tp_descr_get`的解包与绑定机制,揭示了`staticmethod`返回原生函数而`classmethod`返回绑定类对象的bound method的真正原因。

## Atmosphere & style 视频整体呈现出硬核、严谨且循序渐进的技术极客风格。主讲人码农高天语气平实自然,直奔底层原理,毫不拖泥带水,展现出一种“不仅知其然,更要知其所以然”的探究精神。观众跟随着从最基础的Python语法逐渐下潜到C源码结构体与字节码调度,能清晰感受到层层剥茧后的豁然开朗。

## Bulletin - [00:00](https://www.youtube.com/watch?v=j878XzC6J54&t=0s) staticmethod 和 classmethod 均为内置装饰器,但传参机制截然不同:静态方法不隐式注入任何首参数,而类方法无论通过类还是实例调用,首个实参始终自动绑定为当前类对象。 - [05:00](https://www.youtube.com/watch?v=j878XzC6J54&t=300s) 类字典 A.__dict__['f'] 中实际存储的是 staticmethod 或 classmethod 实例,直接通过点号访问时能拿到函数或绑定方法,完全依赖于底层描述器协议(Descriptor Protocol)。 - [05:00](https://www.youtube.com/watch?v=j878XzC6J54&t=300s) 类属性查找遵循专门的 type_getattro 机制,只要查找到的对象定义了 tp_descr_get,无论其是否为数据描述器,都会无条件调用其 __get__ 方法进行解析。 - [10:00](https://www.youtube.com/watch?v=j878XzC6J54&t=600s) classmethod 的描述器在被触发时,会提取当前类或实例所属的 type,并将其与原函数包装成首参数固定的 bound method 返回。

## Key takeaways - [00:00](https://www.youtube.com/watch?v=j878XzC6J54&t=0s) staticmethod 将普通函数归纳在类命名空间中,既不隐式接收 self 也不隐式接收 cls。 - [00:00](https://www.youtube.com/watch?v=j878XzC6J54&t=0s) 通过类名 A.f(...) 或实例对象 a.f(...) 均可调用 staticmethod,两者的执行效果完全一致。 - [00:00](https://www.youtube.com/watch?v=j878XzC6J54&t=0s) classmethod 会自动将类本身作为第一个实参传入,调用者无需显式传递首个参数。 - [00:00](https://www.youtube.com/watch?v=j878XzC6J54&t=0s) 通过实例对象调用 classmethod 时,隐式传入的首个实参依然是该对象的所属类(type),绝不会变成实例对象本身。 - [05:00](https://www.youtube.com/watch?v=j878XzC6J54&t=300s) 直接通过 A.f 访问属性返回的是函数对象,而查询类底层属性字典 A.__dict__['f'] 得到的则是 staticmethod 实例对象。 - [05:00](https://www.youtube.com/watch?v=j878XzC6J54&t=300s) staticmethod 在 CPython 内部对应 PyStaticMethod_Type,属于非数据描述器(Non-data Descriptor)。 - [05:00](https://www.youtube.com/watch?v=j878XzC6J54&t=300s) staticmethod 的描述器实现函数 tp_descr_get 逻辑简单,其核心就是直接解包并返回内部持有的原始 callable 函数。 - [05:00](https://www.youtube.com/watch?v=j878XzC6J54&t=300s) 对类进行属性访问走的是针对 Type 的专属查找路径 type_getattro,而非普通对象的属性查找逻辑。 - [05:00](https://www.youtube.com/watch?v=j878XzC6J54&t=300s) 在 type_getattro 流程中,只要属性对象实现了 tp_descr_get,都会被无条件调用并触发描述器求值。 - [05:00](https://www.youtube.com/watch?v=j878XzC6J54&t=300s) 未被装饰的普通函数自身也是描述器,当通过类访问且 obj 参数为 None 时,直接将原始函数自身原样返回。 - [05:00](https://www.youtube.com/watch?v=j878XzC6J54&t=300s) 普通函数只有在 obj 不为空(通过实例访问)时,其描述器才会返回与该实例绑定的 method 对象。 - [10:00](https://www.youtube.com/watch?v=j878XzC6J54&t=600s) classmethod 的描述器与 staticmethod 不同,它不会返回原始裸函数,而是返回一个绑定了类对象的 bound method。 - [10:00](https://www.youtube.com/watch?v=j878XzC6J54&t=600s) 通过类访问 classmethod 时,type_getattro 会把当前访问的类对象作为第三个参数直接传给描述器的 descr_get。 - [10:00](https://www.youtube.com/watch?v=j878XzC6J54&t=600s) 通过实例访问 classmethod 时,通用属性查找逻辑会获取该实例对应的所属类型 type(a) 并传入描述器完成绑定。

## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=j878XzC6J54&t=0s) staticmethod 与 classmethod 的基本用法与区别

在 Python 中,`staticmethod` 和 `classmethod` 是两个非常常用的内置装饰器(built-in decorator),它们最显著的共同特点是通常被用于类定义(class definition)内部。在普通情况下,我们在类中定义方法时,第一个形参通常约定为 `self`,这是一个在调用方法时自动将调用该方法的对象实例传递进去的机制。然而,`staticmethod` 截然不同。被 `@staticmethod` 装饰的函数实际上就是一个极其普通的函数,其调用方式与在类外部定义的全局函数完全一样,这个装饰器本质上只是将该函数置于类的命名空间下,使其在逻辑组织上从属于这个类。使用 `staticmethod` 的核心优势体现在两方面:第一,它提供了更好的代码封装性,将关联逻辑整合进类命名空间;第二,调用它无需实例化类的对象,可以直接通过 `A.f(...)` 类名加函数名的方式调用。当然,如果手头恰好有一个该类的实例对象,通过 `A().f(...)` 实例来调用也是完全支持的,两者的执行效果毫无差异。

相比之下,`classmethod` 虽然同样是将函数绑定在类本身而非类的实例对象上,但两者的参数传递机制存在明显区别。码农高天特别强调了参数的特性:“我们回忆一下 static method 是没有传进来 class 的,它只有你函数调用时候的 argument,但是 class method 会把这个 class 当作第一个参数传进来。”在实际编写业务逻辑时,被 `@classmethod` 装饰的函数第一个形参约定为 `cls`,后面再接调用参数;而在真正调用时,调用者不需要显式传入类参数,Python 会自动将该类对象作为第一个参数压入。这与普通方法将实例 `self` 作为首个参数自动传入的体验高度相似。

此外,`classmethod` 无论通过类直接调用还是通过实例对象调用,其传参规则保持绝对一致。即便使用 `A()` 实例化对象来调用该方法,底层自动传入的第一个参数依然是该对象所属的类 `A`(即其 `type`),而绝对不会变成实例对象本身。码农高天指出:“一定注意,无论是在 class 上面调用,还是在 object 上面调用,它进来的这第一个 argument,class 都是 class 啊,就即使你是 A 括号建立了一个 object 调用,它的第一个 argument 这个 class 依然是它的 type 也就是 a,这里就是这个 class method 跟我们一般的 method 不同的地方,我们一般的 method 第一个 self 是 object。”

- **staticmethod 的特点**:不隐式传递任何额外参数(既不传 `self` 也不传 `cls`);可以通过 `A.f()` 或 `a.f()` 调用;主要起到将普通功能函数归纳在类命名空间内的封装作用。 - **classmethod 的特点**:隐式传递当前类(`cls`)作为第一个实参;无论使用 `A.f()` 还是 `a.f()` 调用,首参数均为当前类型对象;常用于工厂方法或需要感知类属性的场景。

```mermaid flowchart TD accTitle: 普通方法、staticmethod 与 classmethod 的首参数传递机制对比 accDescr: 展示三种方法在通过类或对象调用时,首个隐式参数传递的差异。

subgraph RegularMethod ["普通方法 (Regular Method)"] R_Obj["obj.method(...)"] -->|"隐式传入"| R_Self["self (实例对象 obj)"] R_Cls["Cls.method(...)"] -->|"需显式传参或报错"| R_Err["无自动绑定"] end

subgraph StaticMethod ["静态方法 (@staticmethod)"] S_Call["Cls.f(...) 或 obj.f(...)"] -->|"无隐式传参"| S_Pure["普通函数执行 (仅接收显式参数)"] end

subgraph ClassMethod ["类方法 (@classmethod)"] C_Cls["Cls.f(...)"] -->|"隐式传入"| C_Class["cls (当前类 Cls)"] C_Obj["obj.f(...)"] -->|"隐式传入实例的 type"| C_Class end ```

---

[05:00](https://www.youtube.com/watch?v=j878XzC6J54&t=300s) 底层机制剖析:为什么打印 a.f 得到的是函数?

在明确两者的表层用法之后,深入 Python 底层的核心机制才是关键。码农高天直言:“那你可能觉得这些内容很简单对吧,或者你之前也听过,那如果你之前看过我的视频就肯定会知道,我视频的重点从来就不在怎么使用它们,而是它们是怎么实现的呢?”

如果直接执行 `print(A.f)`,终端会显示它是一个函数(function)。然而,类定义体内部的代码执行完毕后,所有属性都会被存入类属性字典 `A.__dict__` 中。如果进一步探查底层,打印 `A.__dict__['f']`,就会发现它存入的实际是一个 `staticmethod` 对象(`staticmethod object`)。从 CPython 源码来看,`staticmethod` 本身就是一个内置类型 `PyStaticMethod_Type`。既然保存在字典里的是一个静态方法包装对象,为什么通过 `A.f` 读取时拿到的却是解包后的原始函数?其核心机制在于 Python 的描述器协议(Descriptor Protocol)。

`PyStaticMethod_Type` 结构体中实现了描述器获取函数 `tp_descr_get`。在 staticmethod 的 descriptor get 逻辑中,它仅仅是简单地返回其内部存储的 callable(即初始化时传入的原函数 `sm_callable`)。但是这里引申出了一个极具迷惑性的边界问题:在 Python 的属性查找规则中,一个对象查找属性时通常优先查看是否存在数据描述器(Data Descriptor,即同时实现了 `__get__` 和 `__set__`);而 `staticmethod` 并没有定义 `__set__`,属于非数据描述器(Non-data Descriptor)。按照常规对象的查找机制,Python 应当优先去实例自身的 `__dict__` 中找,那为什么通过类访问属性时依然会触发非数据描述器的 `__get__` 呢?

原因在于对类(Type)进行属性访问时,走的是专门针对类型的查找逻辑——`type_getattro`(对应 C 层的 `PyType_Type.tp_getattro`),而不是普通对象的 `object_generic_getattr`。在执行字节码 `LOAD_ATTR` 查找类的属性时,`type_getattro` 会通过 `_PyType_Lookup` 在类的继承链上查找属性,拿到该属性对象后,若发现它定义了 `tp_descr_get`,无论它是不是数据描述器,都会无条件调用它的描述器 `__get__` 方法。

对于普通类函数而言,当用户直接通过类去获取方法(如访问未被装饰的普通函数 `A.func`)时,拿到的也是一个原始 `function` 而非绑定方法(bound method)。这是因为函数的 `descr_get` 函数在被调用时,接收的第二个参数目标对象 `obj` 为 `NULL`(即在 Python 层表现为 `None`)。当 `obj` 为 `None` 时,函数的描述器实现直接将其自身(即原函数)原样返回;只有当 `obj` 不为空(通过实例访问方法)时,它才会返回一个将对象和函数绑定在一起的 `method` 对象。

- **`A.f` 与 `A.__dict__['f']` 的差异**:`A.__dict__['f']` 绕过描述器机制,直接获取底层字典存储的 `staticmethod` 实例;而 `A.f` 会触发 `tp_descr_get`,返回内部包装的原函数对象。 - **`type_getattro` 查找特性**:类属性访问遵循针对 Type 的专属属性查找逻辑,只要属性存在描述器 `__get__`,就会直接对其求值并返回其结果。

---

[10:00](https://www.youtube.com/watch?v=j878XzC6J54&t=600s) classmethod 的描述器绑定逻辑与总结

理解了 `staticmethod` 的描述器解包过程后,`classmethod` 的实现机制也就迎刃而解。`classmethod` 与 `staticmethod` 的核心实现路径非常相似,但其描述器的 `tp_descr_get` 函数做的事情更为关键:它返回的不再是裸函数,而是通过底层逻辑将原函数与类型对象(type)绑定在一起的绑定方法(bound method)。

在 `type_getattro` 的源码中,当它调用目标描述器的 `descr_get` 时,会将当前访问的类作为第三个参数(`type` 参数)传递进去。由此建立了一条严密的绑定链路: 1. **通过类直接调用(`A.f`)**:`type_getattro` 直接将类 `A` 作为第三个参数传进 `classmethod` 的描述器函数,描述器直接将该函数和该类绑定,返回一个首参数固定为类 `A` 的 bound method。 2. **通过实例对象调用(`a.f`)**:在普通对象的 `object_generic_getattr` 流程中,会检测到类属性字典中包含定义了 `tp_descr_get` 的 `classmethod` 对象,此时获取该实例的所属类 `type(a)` 并一同传入描述器中,描述器同样将函数与该所属类完成绑定。

正是这一段统一的描述器逻辑,使得开发者在调用端无论是直接使用类本身调用,还是使用该类的任何实例对象调用,`classmethod` 底层都可以稳定提取出目标类型本身,从而保证了运行时传给函数的第一个实参永远是类对象本身。

通过对 `staticmethod` 和 `classmethod` 底层机制的拆解,展示了从类字典存储、`LOAD_ATTR` 字节码调度到描述器协议(Descriptor Protocol)协作的全流程,揭示了表面看似简单的语法糖背后严谨的 CPython 底层实现。

## Notable quotes > [00:00](https://www.youtube.com/watch?v=j878XzC6J54&t=0s) static method 是没有传进来 class 的,它只有你函数调用时候的 argument,但是 class method 会把这个 class 当作第一个参数传进来。 — 码农高天

## Mentioned Python, CPython, staticmethod, classmethod, 码农高天

## Video description 这期视频聊一下staticmethod和classmethod,以及它们背后的实现机制。肯定有你没认真想过的角落~

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

中文简报

CPython借描述器协议解析类属性 确保classmethod绑定类型 类属性查找通过底层type_getattro无条件触发描述器协议,使staticmethod实时解包为裸函数,并确保classmethod在任何调用方式下均自动绑定类对象作为首参数。 视频首先对比了staticmethod与classmethod在表层用法上的异同,强调静态方法本质上是置于类命名空间下的普通函数,而类方法则无论通过类还是实例调用都会自动隐式传入当前类型对象。随后,作者深入探究了底层实现,指出类属性字典中实际保存的是内置装饰器包装对象。通过结合CPython源码与描述器协议(Descriptor Protocol),视频详细剖析了type_getattro查找属性时触发tp_descr_get的解包与绑定机制,揭示了staticmethod返回原生函数而classmethod返回绑定类对象的bound method的真正原因。 staticmethod和classmethod均为内置装饰器,但传参机制截然不同:静态方法不隐式注入任何首参数,而类方法无论通过类还是实例调用,首个实参始终自动绑定为当前类对象。 (0:00) 类字典A.__dict__['f']中实际存储的是staticmethod或classmethod实例,直接通过点号访问时能获取函数或绑定方法,完全依赖于底层描述器协议(Descriptor Protocol)。 (5:00) 类属性查找遵循专门的type_getattro机制,只要查找到的对象定义了tp_descr_get,无论其是否为数据描述器,都会无条件调用其__get__方法进行解析。 (5:00) classmethod的描述器在被触发时,会提取当前类或实例所属的type,并将其与原函数包装成首参数固定的bound method返回。 (10:00)

Reading summary

码农高天指出,Python 中的 staticmethod 与 classmethod 表面上是控制参数传递的内置装饰器,其底层则依赖描述器协议与类型属性查找机制来实现函数解包与参数绑定。 表面参数传递规则与调用差异 (0:00) staticmethod 与 classmethod 均定义于类命名空间内部,且都可以通过类名或实例对象调用。被 @staticmethod 装饰的函数表现为普通函数,调用时既不隐式接收实例 self,也不隐式接收类 cls,仅用于将独立函数封装在类命名空间中以提升代码组织性。 相比之下,@classmethod 会隐式接收类本身作为第一个实参。码农高天强调,无论通过类本身(A.f)还是通过实例对象(A().f)调用,classmethod 传入的第一个参数永远是其所属的类型对象(type),而绝非实例本身。这使得 classmethod 保持了统一的类绑定特性,常用于工厂方法或需要感知类属性的场景。 staticmethod 的描述器解包机制 (5:00) 虽然通过类访问属性 A.f 获取到的是一个原始函数,但类属性字典 A.__dict__['f'] 中存储的实际上是内置类型 PyStaticMethod_Type 的实例。由于 staticmethod 仅实现了描述器获取函数 tp_descr_get 而未定义 __set__,在分类上属于非数据描述器。 在底层执行 LOAD_ATTR 字节码时,类属性查找会调用专门针对类型的 type_getattro。该查找机制在通过 _PyType_Lookup 检索到属性后,只要发现其定义了 tp_descr_get,便会无条件调用该描述器的获取方法。对于 staticmethod 而言,其描述器内部逻辑仅仅是直接返回其初始包装的原函数 sm_callable,从而在类属性访问时完成解包。 普通方法与 classmethod 的绑定过程 (10:00) 普通类函数在通过类访问(A.func)时,其描述器接收到的目标对象 obj 为 None,因而直接原样返回自身;仅在通过实例调用时才会生成将函数与实例绑定的 bound method。而 classmethod 的描述器实现机制走的是另一条路径。 当通过类调用 classmethod 时,type_getattro 会将当前类对象作为类型参数传入描述器的 descr_get;当通过实例调用时,普通对象的查找流程 object_generic_getattr 会提取实例的所属类型 type(a) 传入描述器。classmethod 的描述器内部逻辑会将原函数与传入的类对象绑定,生成首参数固定为该类的 bound method,确保了两端调用时参数传递行为的高度一致。 该解析通过展示类属性字典存储、type_getattro 属性查找路径以及描述器协议的交互,证明了 staticmethod 与 classmethod 并非单纯的语法糖,而是 CPython 借助统一的描述器框架实现的底层解包与方法绑定机制。

Source quotes

[0:00] 码农高天: static method 是没有传进来 class 的,它只有你函数调用时候的 argument,但是 class method 会把这个 class 当作第一个参数传进来。

Original transcript

[0:00] 这期视频我们来讲两个built-in的decorator一个叫做static method,还有一个叫做class method这两个decorator有一个共同的特点就是他们都会被用在class的definition里面我们先来说这个static method我们看左边我定义了一个class Aclass A 里面有一个函数 F,这个 F take 1 argument x那这里我们注意一下我们一般情况下在class里面定义method的时候第一个argument都是self对不对这个我们前面也讲过是一个把调用这个函数的对象传进去的机制那对于static method就不一样了我们可以认为,一个定义在class里面的static method就是一个很普通的函数我们使用它的时候就像使用其他定义在外面的正常函数是一样的而这个Static method decorator更多的是把这个函数放到了这个class的命名空间下也就是让这个函数从属于这个类了我们看一下效果在这个f被static method给装饰之后我们调用A.F1注意这里A是一个class不是这个class object我们看到他打印了1那这个用法就很像我们在全局定义一个fx然后再去调用F1他们的结果是一样的那Static Method的意义呢 第一是它提供了一个更好的封装就这个函数现在从属于一个类了第二我们可以发现我们并不用建立这个类的一个object我们可以直接用类来调用这个static method那在很多时候我们想把一个功能绑定在一个类上 而不是这个类的对象上的时候 它就很有用当然,这个Static method不光是在这个类上可以调用,在这个类的对象上它也可以调用我们看我们建立了一个这个类的对象 A 括号 他调用 F 的效果是一样的也就是说如果你恰好有了一个这个类的对象 你依然可以使用这个static method好,我们接着说class methodclass method跟static method其实很像它同样也是把一个函数给绑定到这个类上而不是这个类产生了对象上它们的区别在于class method,它的第一个argument是这个class。我们回忆一下static method是没有传进来class的它只有你函数调用时候的argument但是class method会把这个class当作第一个参数传进来当然你在实际调用的时候是不传这个class参数的这里他跟我们正常定义的method self是非常非常像的我们看左边的代码稍微把f改了一下,现在f是两个argument一个class,一个x我们调用的方式依然是A.F1我们看结果他打印出来了这个class 也就是a这个class还有我们给的这个argument一和 Static Method一样class method也可以在object上直接调用我们看我们建立了一个A的Object然后用它来调用F1结果跟刚才是一样的一定要注意,无论是在class上面调用,还是在object上面调用,它进来的这第一个argumentclass都是class啊就即使你是 A 括号建立了一个 object 调用它的第一个argument这个class依然是它的type 也就是a这里就是这个class method跟我们一般的method不同的地方我们一般的method第一个cell是object好那你可能觉得这些内容很简单对吧或者你之前也听过那如果你之前看过我的视频就肯定会知道我视频的重点从来就不再怎么使用它们而是它们是怎么实现的呢Static method跟class method实现的方式是非常相似的我们用相对更简单的static method来举个例子第一个问题 定义在A里面的这个F是什么你肯定会说这个简单我们只要把a.f打印出来就行了对不对好 我们打印a.f发现它是一个function然而它真的是一个方式吗我们知道在class A 里面定义的成员最终会被保存到A的下游行下游行dict里面我们尝试打印一下A点DictI've他是什么呢?我们可以看到实际上它是一个static [5:00] Method object. We are not supposed to be surprised. Because we talked about decorator We know that the design of the design of the form of the form of the form of the form of the form of the form of the form of the form. 那如果说static method是一个类或者说是一个type的话 That f is not static method object. This is a standard of "lay" and "lay" of the "stress" Let's look at Python's original name. It's very clear. Static method. is a static method type That if f is a static method object, How can this function be performed? Why we use a.f to find a function? That's what I'm watching in my videos. You can definitely think It's behind the "描述器" We can see here is PyStatic method type definition. It has a "descriptor get" method This number is very simple, it is just back to the SM callable That is not a good idea of the SM callable just in static method object In initialization, we will put the f to the f So, if we use static method object, 它的 descriptor get 函数 will get our function That's what we've already talked about A class of object, in the load ATTR, how to use the descriptor If you don't forget to watch my previous video But you have thought, why am I doing a function of a.f? It is still a function of the descriptor? We mentioned that an object in a certain situation will first find data descriptor 也就是看 descriptor有没有定义set 然而我们刚才可以看到这个static method object是没有set descriptor的 So you may think it will be to Dict. and in dict, you will find the static method object 但是 We must be careful 作为一个class or type Python actually for it to be a specialized get-attr. We can see type get attro is type get attro So when he does load ATTR It's the use of the number of objects and the number of objects are not the same Let's look at the type of get atro He said that this number of the object generic get ATTR is very similar 但是 It will use PyType Lookup and not just access TP-Dict 实际上我们可以看到 他在拿到这个 attribute之后 是会去直接找他的 descriptor get函数的 当你在对一个class一个类 use the attribute access to a.f. he will always use the attribute descriptor get rule. That you may have a question Why did we have before we used method? We can just use the layer of the layer 拿到的是一个function,而不是一个method呢? We mentioned that the function is a bound method method. just through the function descriptor This is the reason 大家看,在他调用这个 descriptor get 函数的时候 The argument is that he gave a law. On the note, it shows that For the node, the descriptor is in target object. That's when we use the lay.s of the name of the name, we also use the descriptor 只不过这个argument我们给进去的是一个naught,而不是在使用method时候的self Let's look at function descriptor get. when the argument is obj is now, it will be the same way to the number of the argument This is why we usually use the "lay". It is a number of variables, not a method. Class method and static method are very similar but its descriptor number It's not the Colourable and [10:00] 把Hullible给绑定到了自己这个type身上我们回到这个type的getHTRO可以看到他把type作为第三个argument传到这个descriptor里因此在这里当你是通过类第二函数名调用的时候你会直接把type传进来当你通过对象.方法名调用的时候他会通过这个对象拿到这个类最后实际上是这一段代码把这个函数和这个类给绑定到一起的最后就实现了我们前面提到的无论是类本身调用还是它的对象调用第一个argument传进去的都是这个class的效果好 那今天视频就到这里 希望对大家有所帮助 如果喜欢的话 记得点一下关注 别忘了一键三连哦字幕志愿者 杨茜茜