Python 属性访问与操作全解析:内置函数、魔法方法与描述符深度指南

目录

前言

属性操作是 Python 面向对象编程的核心基础。本文系统性梳理 Python 属性读写删三大内置函数、底层魔法方法、描述符协议的完整工作机制

一、基础概念

在 Python 中,一切皆对象,实例、类、模块、函数均支持属性操作。Python 为属性操作提供了三层标准化实现:

  • 上层接口:getattr()/setattr()/delattr() 内置函数(安全操作属性)
  • 中间钩子:__getattribute__/__setattr__/__delattr__/__getattr__ 魔法方法
  • 底层协议:描述符协议(__get__/__set__/__delete__

二、Python 属性两大查找链路

2.1 链路一:方法查找链(解释器级别)

作用 :解释器寻找「属性操作的执行者」------ 底层魔法方法

触发时机 :每次执行属性读写删操作时,最先执行

查找规则:

  1. 针对当前对象的类,走 MRO 继承链(包含当前类)层层向上查找
  2. 中途任意父类重写了对应魔法方法,立即停止查找,执行当前类的重写方法
  3. 整条链路无重写,最终找到顶层基类 object 的原生魔法方法

2.2 链路二:业务属性查找链(方法内部级别)

作用 :魔法方法内部,寻找用户真正需要的「业务属性」(name/age/ 自定义方法)

触发时机 :解释器找到魔法方法后,方法内部执行

查找规则(实例属性)

  1. 查找类及其父类中的 数据描述符(__delete__或__set__
  2. 其次:查找实例自身 __dict__
  3. 再次:查找非数据描述符、普通类属性
  4. 最后:沿着 MRO 查找父类
  5. 查找失败:抛出 AttributeError 异常

2.3 双链路执行顺序

  1. 先找方法:解释器通过 MRO 链,找到 __getattribute__ 执行者
  2. 再找属性:执行者内部,通过属性链找到目标业务属性

三、三大属性操作完整机制

3.1 获取属性

上层调用方式

语法糖:obj.属性名

内置函数:getattr(obj, name, default=None)

完整执行流程

  1. 解释器通过 MRO 方法查找链 找到 __getattribute__
  2. 执行 __getattribute__,按照业务属性查找链检索属性
  3. 查找成功:直接返回属性值
  4. 查找失败:抛出异常 → 触发 __getattr__ 兜底方法

底层原生伪源码(object 原生实现)

python 复制代码
class object:
    def __getattribute__(self, name: str):
        """
        Python 官方正确版 __getattribute__ 实现
        修复:数据描述符优先级 > 实例属性
        """
        cls = type(self)

        # ======================
        # 第一步:最高优先级 → 查找【数据描述符】(类中定义)
        # ======================
        for base in cls.__mro__:
            if name in base.__dict__:
                attr = base.__dict__[name]
                # 数据描述符:同时实现 __get__ 和 __set__
                if hasattr(attr, "__set__") or hasattr(attr, "__delete__"):
                    if hasattr(attr, "__get__"):
                        return attr.__get__(self, cls)

        # ======================
        # 第二步:查找【实例自身属性】
        # ======================
        if name in self.__dict__:
            return self.__dict__[name]

        # ======================
        # 第三步:查找【非数据描述符 / 普通类属性】
        # ======================
        for base in cls.__mro__:
            if name in base.__dict__:
                attr = base.__dict__[name]
                if hasattr(attr, "__get__"):
                    return attr.__get__(self, cls)
                return attr

        # ======================
        # 第四步:全部失败,抛错触发 __getattr__
        # ======================
        raise AttributeError(f"'{cls.__name__}' object has no attribute '{name}'")

兜底方法 getattr

仅在属性查找失败、抛异常后触发,用于自定义兜底逻辑,不会主动执行:

python 复制代码
class Demo:
    def __getattr__(self, name):
        # 不存在的属性统一返回默认值
        return None

obj = Demo()
print(obj.abc) # None

3.2 设置属性

上层调用方式

语法糖:obj.属性名 = 值

内置函数:setattr(obj, name, value)

完整执行流程

  1. 解释器通过 MRO 方法查找链 找到 __setattr__
  2. 执行底层赋值逻辑
  3. 优先判断:是否存在数据描述符,存在则执行描述符 __set__
  4. 无描述符:直接写入实例 __dict__

底层原生伪源码

python 复制代码
class object:
    def __setattr__(self, name: str, value):
        cls = type(self)
        # 1. 数据描述符优先级最高,优先执行
        for base in cls.__mro__:
            if name in base.__dict__:
                attr = base.__dict__[name]
                if hasattr(attr, "__set__"):
                    attr.__set__(self, value)
                    return
        # 2. 无描述符,写入实例字典
        self.__dict__[name] = value

3.3 删除属性

上层调用方式

语法糖:del obj.属性名

内置函数:delattr(obj, name)

完整执行流程

  1. 解释器通过 MRO 方法查找链 找到 __delattr__
  2. 优先判断:是否存在带 __delete__ 的数据描述符
  3. 存在则执行描述符删除逻辑
  4. 不存在则直接删除实例 __dict__ 中的属性

底层原生伪源码

python 复制代码
class object:
    def __delattr__(self, name: str):
        cls = type(self)
        # 1. 优先执行数据描述符 __delete__
        for base in cls.__mro__:
            if name in base.__dict__:
                attr = base.__dict__[name]
                if hasattr(attr, "__delete__"):
                    attr.__delete__(self)
                    return
        # 2. 删除实例属性
        if name in self.__dict__:
            del self.__dict__[name]
        else:
            raise AttributeError()

3.4 检查属性

上层调用方式

语法:hasattr(obj, name: str) -> bool

作用:安全判断对象是否包含指定属性,返回布尔值

完整执行流程

  1. 底层调用 getattr(obj, name) 尝试获取属性
  2. 属性获取成功 → 返回 True
  3. 抛出 AttributeError → 返回 False
  4. 会完整触发 __getattribute__ 和 __getattr__ 逻辑

底层原生伪源码

python 复制代码
def hasattr(obj, name: str) -> bool:
    """
    hasattr 官方等效实现
    本质:封装 getattr,捕获异常
    """
    try:
        getattr(obj, name)
        return True
    except AttributeError:
        return False

四、描述符协议(底层扩展机制)

描述符是 Python 实现属性拦截、校验、托管的底层协议,property、staticmethod 均基于描述符实现。

4.1 描述符分类

数据描述符(Data Descriptor)

实现了 __set__ 或 __delete__ 任意一个方法 → 就是数据描述符(不需要 __get__

非数据描述符(Non-data Descriptor)

仅实现 __get__,没有 __set__ / __delete__

4.2 描述符介入后的执行规则

描述符逻辑嵌入在 __getattribute__ / __setattr__ / __delattr__ 内部,优先级高于普通属性,会重写默认属性行为。

4.3 完整实战示例

python 复制代码
# 自定义数据描述符
class FieldDescriptor:
    def __get__(self, instance, owner):
    # self:描述符实例自身
    # instance:被代理对象
    # 被代理对象的类
        print("执行描述符读取 __get__")
        return self.val

    def __set__(self, instance, value):
        print("执行描述符赋值 __set__")
        self.val = value

class User:
    # 绑定描述符
    age = FieldDescriptor()

obj = User()
obj.age = 18    # 触发描述符 __set__
print(obj.age)  # 触发描述符 __get__

五、终极完整执行链路汇总

5.1 属性读取全链路

  1. 执行 obj.name / getattr(obj, "name")
  2. 解释器 MRO 查找 __getattribute__(重写优先,最终兜底 object)
  3. 执行 __getattribute__,内部检索业务属性
  4. 描述符 → 实例属性→类属性 → 父类属性
  5. 查找失败抛错 → 触发 __getattr__ 兜底

5.2 属性赋值全链路

  1. 执行 obj.name = val / setattr(obj, "name", val)
  2. 解释器 MRO 查找 __setattr__
  3. 优先执行数据描述符 __set__
  4. 无描述符则写入实例 __dict__

5.3 属性删除全链路

  1. 执行 del obj.name / delattr(obj, "name")
  2. 解释器 MRO 查找 __delattr__
  3. 优先执行描述符 __delete__
  4. 无描述符则删除实例属性
相关推荐
Arenaschi1 小时前
关于GPT的版特点
java·网络·人工智能·windows·python·gpt
人道领域1 小时前
【LeetCode刷题日记】108.将有序数组转换为二叉搜索树
java·算法·leetcode
橙淮1 小时前
并发编程(五)
java
MAXrxc1 小时前
ospf笔记
网络·笔记
薛定猫AI1 小时前
【深度解析】Hermes Agent Velocity Release:长期记忆、自进化技能与多智能体任务编排实践
网络·人工智能
Leweslyh2 小时前
基于 Confucius 架构的无人集群网络控制原语解析
开发语言·网络·php
过期动态2 小时前
【LeetCode 热题 100】无重复字符的最长子串
java·数据结构·spring boot·算法·leetcode·职场和发展
Yeats_Liao2 小时前
好复杂的 IoT 世界:工业数据采集技术栈全景解析
java·物联网·struts
古月方枘Fry2 小时前
OSPF 企业级多区域网络
运维·服务器·网络