函数的描述符特性与绑定方法的生成机制

news/2025/11/5 18:45:03/文章来源:https://www.cnblogs.com/wangya216/p/19194412

函数的描述符特性与绑定方法的生成机制

一、为什么把两件事放在同一篇讲

在 Python 中,「函数」(function)本身是一种非数据描述符(non-data descriptor)。
解释器把函数放进类属性槽里时,正是靠描述符协议把它**魔术般地」变成「绑定方法」(bound method)。
理解描述符是理解「方法绑定」的唯一入口;理解绑定过程又能反向验证描述符的工作方式。二者不可分割。


二、描述符协议(descriptor protocol)速览

协议成员 是否必须 调用时机 作用
__get__(self, obj, objtype=None) 必须 读取属性时 返回「计算后的值」
__set__(self, obj, value) 可选 赋值时 拦截写操作
__delete__(self, obj) 可选 del 时 拦截删除
  • 数据描述符:至少实现 __set____delete__;优先级高于实例字典。
  • 非数据描述符:只实现 __get__;优先级低于实例字典,高于类字典的普通值。

三、函数对象:一个典型的非数据描述符

CPython 源码:Objects/funcobject.c
PyFunction 结构体里自带:

PyDescrObject f_descr;   /* 嵌入的 descriptor 头部 */

因此所有函数天生带 __get__,签名:

function.__get__(self, obj, objtype=None) -> method
  • obj is None → 返回未绑定函数本身(Python 3 里就是原函数)。
  • obj is not None → 返回绑定方法,把 obj 作为第一个参数(self)固化。

四、绑定方法(bound method)的生成过程

  1. 类属性检索
    MyClass.spam 触发 type.__getattribute__PyType_Lookup 找到类字典里的函数对象 spam

  2. 描述符触发
    因为函数实现了 __get__,解释器转而执行:
    method = spam.__get__(None, MyClass) # 未绑定

    method = spam.__get__(instance, MyClass) # 绑定

  3. 方法对象诞生
    CPython 内部新建一个 PyMethodObject,保存:

    • im_func → 原函数指针
    • im_self → 绑定的实例(或 NULL)
    • im_class → 所属类
      这一步对用户完全透明。
  4. 调用阶段
    绑定方法被执行时,它的 __call__im_self 插到参数列表最前面,再转发给 im_func


五、代码级演示:从函数到绑定方法

class Foo:def bar(self, x):return x * 2f = Foo()
print(Foo.bar)   # <function Foo.bar at ...>   (未绑定)
print(f.bar)     # <bound method Foo.bar of <__main__.Foo object ...>>

验证描述符身份:

>>> Foo.bar.__get__(None, Foo) is Foo.bar
True
>>> f.bar.__func__ is Foo.bar
True
>>> f.bar.__self__ is f
True

六、静态方法与类方法:描述符的「二次包装」

staticmethod / classmethod 同样是描述符,只是它们在 __get__不返回原函数,而是返回:

  • staticmethod:原函数(无绑定)
  • classmethod:绑定到类对象的新方法

源码级等价:

class staticmethod:def __init__(self, func):self.func = funcdef __get__(self, obj, objtype=None):return self.funcclass classmethod:def __init__(self, func):self.func = funcdef __get__(self, obj, objtype=None):if objtype is None:objtype = type(obj)return self.func.__get__(objtype, objtype)

因此:
函数 → 描述符 →(被 staticmethod/classmethod 再次包装)→ 新的描述符
形成一条「描述符链」。


七、优先级现场实验

class A:def f(self): pass          # 非数据描述符a = A()
a.f = 123                     # 实例字典覆盖
print(a.f)                    # 123
del a.f                       # 删除后恢复描述符
print(a.f)                    # <bound method A.f ...>

把函数升级为数据描述符:

class DataDescriptor:def __get__(self, obj, objtype=None):return 42def __set__(self, obj, value):passclass B:f = DataDescriptor()b = B()
b.f = 99
print(b.f)   # 42,优先级:数据描述符 > 实例字典

八、CPython 源码级鸟瞰(快速索引)

文件 关键函数 说明
Objects/funcobject.c func_descr_get 函数描述符入口
Objects/classobject.c method_call 绑定方法执行
Objects/typeobject.c type_getattro 属性检索总控
Python/ceval.c _PyMethodDef_RawFastCall 方法调用加速

九、常见面试速答模板

Q: “Python 的函数写在类里就能自动变成方法,底层是怎么做到的?”
A:
函数本身是非数据描述符,实现了 __get__(self, obj, cls)
类属性检索时,解释器发现它带描述符协议,于是把 obj 传进去;
__get__ 返回一个新对象——绑定方法,内部保存原函数与实例。
调用阶段,绑定方法把实例插到参数最前面,再转发给原函数,于是看似“自动传 self”。


十、结论一句话

函数 →(描述符协议)→ 绑定方法;
静态/类方法 →(再包装成描述符)→ 改变绑定规则。
整个“方法”概念在 Python 里完全是描述符协议的副作用,无魔法,唯协议。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/957062.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

猴子测试

1.核心思想:让测试像一只猴子在胡乱敲击键盘一样,完全随机地、无目的地对软件进行操作,以发现那些在常规、有逻辑的测试中难以发现的深层错误。 2.目的:不是为了验证功能是否正确,而是为了测试软件的健壮性、稳定…

如何选择适合的海外外呼系统电销服务商?

随着越来越多企业将业务拓展到海外市场,外呼系统成为连接客户与企业的重要桥梁。然而,跨境通信的复杂性远高于国内,不同国家的通信法规、时区差异、语音质量、合规要求等问题都可能影响销售效果。许多企业在选择海外…

循环队列通用模版

循环队列: RingQ.h#ifndef RINGQ_H #define RINGQ_H#include <stdint.h> #include <stddef.h> #include "stm32g4xx_hal.h"#ifndef RINGQ_DEFAULT_CAP #define RINGQ_DEFAULT_CAP (2048U) /…

如何选择一个人工智能项目

如何选择一个人工智能项目1.可以使用人工智能完成的项目,并且具有很大的业务价值。

Flutter 开发文档

Flutter 开发文档https://docs.flutter.cn/resources/architectural-overview demo: https://github.com/flutter/samples/tree/main/gemini_tasks

别再只用S3了!RustFS的权限管理系统更安全?

别再只用S3了!RustFS的权限管理系统更安全?在数据安全事件频发的2025年,传统S3兼容存储的权限控制已显乏力。而当一款国产开源存储系统RustFS以其精细化的IAM模块实现三级权限控制和​实时安全策略,是否意味着对象…

STL初识project11

STL基本概念 为了建立数据结构和算法的一套标准,诞生了STL STL(Standard Template Library标准模板库) STL从广义分为容器(container)算法(algorithm)迭代器(iterator) 容器和算法之间通过迭代器进行无缝衔接 S…

告别漫长GC停顿:深入解析G1如何实现可预测的毫秒级响应

告别漫长GC停顿:深入解析G1如何实现可预测的毫秒级响应G1(Garbage-First)垃圾回收器是一款面向服务端应用、为大内存和多处理器系统设计的革命性垃圾回收器。G1的核心设计目标是在满足高吞吐量的同时,建立一个“可…

CSS 中 overflow 属性的两个分属性 overflow-x 和 overflow-y 互相影响问题

CSS 中 overflow 属性的两个分属性 overflow-x 和 overflow-y 互相影响问题修改 CSS3 中的 overflow-x 属性,会影响同一元素的 overflow-y 属性。 举例: <div class="relative overflow-y-scroll overflow-x…

C#项目工程文件中,删除两头相同字符串,中间不一样的内容

示例: /// <para>Представляет TCP-прослушиватель, который ожидает подключения клиентов.</para>输入匹配:///\s*<para>[^<]*…

Day13显示模式

标签在网页的显示方式称为显示模式 共有三种不同的显示模式,其作用也各不相同<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="vie…

人工智能加持,海外市场无限可能!AI外呼助您轻松拓展全球业务!

在全球化竞争愈发激烈的今天,越来越多企业将目光投向海外市场。然而,真正踏出国门后才会发现,跨境业务的挑战远比想象中复杂。语言沟通障碍、时区差异、客户响应慢、人工成本高,这些都成为阻碍企业有效拓展海外客户…

从编码到部署:5大AI工具盘活你的全栈开发流程

​ 全栈开发者的日常,往往是在不同工具间不断切换。今天,我们来聊聊那些真正能提升效率的AI工具,看看它们如何各司其职,又在哪些场景下能协同作战。 一、前端原型:快速构思的得力助手 在前端构思阶段,一些AI工具…

如何是一个人工智能公司

如何是一个人工智能公司在公司业务层面,使用人工智能手段并达到了比较优秀的程度,带来了显著价值。

虚拟中间号和手机号有什么区别?

在企业通信和客户隐私保护场景中,“虚拟中间号”这个词越来越常被提及。很多企业在选择通信方案时,往往会纠结:虚拟中间号与普通手机号到底有什么区别?两者能否互换?其实,这背后牵涉的不仅仅是号码本身,更是企业…

关于OpenGL在AMD设备无法显示内容的解决方法

无任何报错,但是看不到任何渲染的东西,源程序在Intel CPU+Nv卡上没有问题。 使用的是4.3版本. 此时调试输出内容为 GL Renderer: AMD Radeon(TM) Graphics GL Version: 4.6.0 Compatibility Profile Context 25.10.2…

超越代码补全:5个能理解你项目上下文的AI编程伙伴

​ 随着AI编程工具的不断进化,它们正在从提供简单代码补全的"助手",转变为能够深度理解项目上下文、协助完成复杂工程任务的"伙伴"。今天我们将盘点5款在这方面表现突出的AI编程工具,看看它们如…

共绩算力 vscode git笔记

# git clone https://github.com/ultralytics/yolov5.git Cloning into yolov5... error: RPC failed; curl 16 Error in the HTTP2 framing layer fatal: expected flush after ref listinggit config --global http.…

WPF 的ListBox 去除默认的Item项,鼠标hover的背景颜色

一、发现的问题 1、最近在做一个新的桌面应用:多屏协同。里边的UI好多使用到了ListBox的。如下图所示 2、使用的Xmal的样式如下:<!--设备列表--><Border Grid.Row="0" Grid.Column="1"…

不止高精度!正点原子 EL15 深度解析:精度、性价比全拉满!

不止高精度!正点原子 EL15 深度解析:精度、性价比全拉满! 在电源测试、电池容量校准、元器件老化验证等场景中,电子负载是不可或缺的核心工具。但市面上同功率段产品要么精度不足,要么价格居高不下,还常存在操作…