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. 无描述符则删除实例属性
相关推荐
nanxun8866 小时前
记一次诡异的 Docker 容器"串包"故障排查
java
金銀銅鐵6 小时前
[Python] 从《千字文》中随机挑选汉字
后端·python
用户1563068103518 小时前
Day01 | Java 基础(Java SE)
java
行者全栈架构师10 小时前
Maven dependency:tree 的 8 个高级用法
java·后端
cup1111 小时前
[技术复盘] Windows Python 打包实战:Nuitka 环境踩坑总结与 CI 自动化构建全指南
python·ai·环境变量·ci·nuitka·skill
aqi0013 小时前
15天学会AI应用开发(七)有了大模型为什么还要引入RAG
人工智能·python·大模型·ai编程·ai应用
行者全栈架构师14 小时前
IDEA 中 Maven 项目的 15 个红色报错快速解决方法
java·后端
令人头秃的代码0_014 小时前
mac(m5)平台编译openjdk
java
金銀銅鐵15 小时前
用 Python 实现 Take-Away 游戏
python·游戏