Python 属性查找:深入理解__getattribute__与__getattr__

目录

一、__getattribute__方法详解

1.1 基本概念

1.2 示例分析

1.3 注意事项

二、__getattr__方法详解

2.1 基本概念

2.2 示例分析

2.3 注意事项

三、__getattribute__与__getattr__的区别对比

3.1 调用时机

3.2 应用场景

3.3 性能影响

四、属性查找顺序


属性查找是一个基础而又关键的操作,当我们使用点号(.)来访问对象的属性时,比如obj.attr,Python 会在对象及其所属类的层次结构中进行搜索,以找到对应的属性值。在这个过程中,__getattribute__和__getattr__这两个特殊方法起着至关重要的作用。理解它们的工作机制和区别,对于编写高效、健壮的 Python 代码至关重要。本文将深入探讨这两个方法,帮助大家掌握它们的用法。

一、__getattribute__方法详解


1.1 基本概念

__getattribute__是 Python 中的一个特殊方法,属于新式类(在 Python 2.2 及以上版本中,所有类默认都是新式类)。

当我们尝试访问对象的任何属性时,无论该属性是否存在,Python 都会首先调用对象的__getattribute__方法。这个方法的定义如下:

def __getattribute__(self, name):pass

其中,self是对象自身的引用,name是要访问的属性名称,它是一个字符串。例如,当执行obj.attr时,name的值就是'attr'。

1.2 示例分析

class MyClass:def __init__(self):self.attribute = "Hello, World!"def __getattribute__(self, name):print(f"正在访问属性: {name}")return super().__getattribute__(name)obj = MyClass()
print(obj.attribute)

在上述代码中,我们定义了一个MyClass类,并在其中重写了__getattribute__方法。当创建obj实例并访问其attribute属性时,__getattribute__方法会首先被调用。它打印出正在访问的属性名称,然后通过super().__getattribute__(name)调用父类(即object类)的__getattribute__方法来获取实际的属性值。这样做是为了避免在__getattribute__方法内部出现无限递归调用。如果我们在__getattribute__方法中直接使用self.name来获取属性值,会再次触发__getattribute__方法,从而导致递归错误。

1.3 注意事项

在重写__getattribute__方法时,一定要注意避免递归错误。如前面提到的,不要在方法内部使用self.attr这种方式来获取属性值,而应该使用super().__getattribute__(attr)。另外,__getattribute__方法无论属性是否存在都会被调用,这意味着即使访问一个不存在的属性,也会进入这个方法。如果在__getattribute__方法中没有找到对应的属性,并且没有抛出AttributeError异常,那么__getattr__方法将不会被调用。

二、__getattr__方法详解


2.1 基本概念

__getattr__同样是 Python 中的特殊方法。

与__getattribute__不同,__getattr__只有在常规的属性查找(在对象的__dict__和类的__dict__及其父类的__dict__中查找)失败,并且对象所属的类定义了__getattr__方法时才会被调用。它的定义如下:

def __getattr__(self, name):pass

self和name的含义与__getattribute__方法中的相同。当 Python 在对象及其类的层次结构中找不到指定的属性时,就会调用__getattr__方法,尝试通过这个方法来获取属性值或者提供默认行为。

2.2 示例分析

class MyClass:def __init__(self):self.attribute = "Hello, World!"def __getattr__(self, name):if name == "nonexistent_attribute":return "这个属性不存在,但我可以返回一个默认值"raise AttributeError(f"'MyClass' object has no attribute '{name}'")obj = MyClass()
print(obj.attribute)
print(obj.nonexistent_attribute)

在这个例子中,我们定义了MyClass类并实现了__getattr__方法。当访问obj的attribute属性时,由于该属性存在于对象的__dict__中,所以__getattr__方法不会被调用,直接返回属性值。而当访问nonexistent_attribute属性时,常规属性查找失败,Python 会调用__getattr__方法。在__getattr__方法中,我们检查属性名称,如果是nonexistent_attribute,则返回一个默认值;否则,抛出AttributeError异常,表明对象确实没有该属性。

2.3 注意事项

__getattr__方法主要用于处理不存在的属性访问。在实现这个方法时,要确保对于确实不存在的属性抛出AttributeError异常,这样可以保持 Python 属性查找机制的一致性,也便于其他开发者理解和调试代码。另外,__getattr__方法只有在常规属性查找失败时才会被调用,这与__getattribute__方法的调用时机有明显区别。

三、__getattribute__与__getattr__的区别对比


3.1 调用时机

  • __getattribute__:在每次访问对象属性时都会被调用,无论该属性是否存在于对象及其类的层次结构中。
  • __getattr__:只有在常规属性查找(在对象的__dict__和类的__dict__及其父类的__dict__中查找)失败后才会被调用。可以说__getattr__是属性查找的 “后备方案”。

3.2 应用场景

  • __getattribute__:适用于需要对所有属性访问进行统一拦截和处理的场景,比如实现属性访问日志记录、权限检查等。例如,我们可以在__getattribute__方法中记录每次属性访问的时间、访问者信息等。
  • __getattr__:主要用于为不存在的属性提供默认行为,例如实现懒加载(只有在真正访问某个属性时才去加载相关资源)、代理访问(通过一个代理对象来访问其他对象的属性)等。比如,在一个数据库连接对象中,可以使用__getattr__方法,当访问某个表名属性时,如果该表对象尚未加载,就自动进行加载操作。

3.3 性能影响

由于__getattribute__方法在每次属性访问时都会被调用,所以它对性能的影响相对较大。尤其是在一个对象有大量属性访问操作的情况下,如果在__getattribute__方法中进行了复杂的逻辑处理,可能会导致性能瓶颈。而__getattr__方法只有在属性查找失败时才会被调用,因此在大多数情况下,它对性能的影响较小。所以,在选择使用哪个方法时,除了考虑功能需求,也要考虑性能因素。例如,在一个性能要求较高且大部分属性都存在的场景中,应尽量避免在__getattribute__方法中添加过多复杂逻辑;而在一个属性访问不确定性较大,且需要为不存在的属性提供默认行为的场景中,__getattr__方法是更好的选择。

四、属性查找顺序


当我们执行obj.attr这样的属性访问操作时,Python 的属性查找顺序如下:

  1. 调用__getattribute__方法:无论属性是否存在,首先调用对象的__getattribute__方法。如果在这个方法中通过super().__getattribute__(name)成功获取到属性值,则返回该值,查找过程结束。如果在__getattribute__方法中抛出了AttributeError异常(例如,属性确实不存在或者在__getattribute__方法内部逻辑判断该属性不应该被访问),则进入下一步。
  2. 检查数据描述符:如果属性是类中的数据描述符(即实现了__get__、__set__和__delete__方法的描述符),则调用数据描述符的__get__方法获取属性值,查找过程结束。
  3. 查找实例属性:在对象的__dict__中查找属性。如果找到,则返回该属性值,查找过程结束。
  4. 检查类属性和非数据描述符:在类的__dict__及其父类的__dict__中查找属性。如果找到的属性是一个非数据描述符(只实现了__get__方法的描述符),则调用其__get__方法获取属性值;如果找到的不是描述符属性,则直接返回该属性值,查找过程结束。
  5. 调用__getattr__方法:如果上述所有步骤都没有找到属性,并且对象所属的类定义了__getattr__方法,则调用__getattr__方法。如果__getattr__方法返回了一个值,则返回该值;如果__getattr__方法抛出了AttributeError异常,则最终抛出该异常,表示属性不存在。

为了更清晰地展示这个过程,我们可以用表格形式总结如下:

步骤

操作

说明

1

调用__getattribute__

首先执行,无论属性是否存在

2

检查数据描述符

若属性是数据描述符,调用其__get__方法

3

查找实例属性

在对象的__dict__中查找

4

检查类属性和非数据描述符

在类及其父类的__dict__中查找,区分非数据描述符和普通属性

5

调用__getattr__

若上述步骤均未找到,且类定义了该方法,则调用

理解这个属性查找顺序,对于深入掌握__getattribute__和__getattr__的工作原理以及它们在属性查找过程中的作用至关重要。

通过本文的介绍,相信大家对 Python 中的__getattribute__和__getattr__方法有了更深入的理解。它们虽然都是与属性查找相关的特殊方法,但在调用时机、应用场景和对性能的影响等方面存在明显区别。在实际编程中,我们应根据具体需求合理选择使用这两个方法,以编写出高效、优雅的 Python 代码。

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

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

相关文章

打表法从原理到实战详解

打表法结合经典案例从原理到实战详解 一、打表法基本信息1.1 打表法定义1.2 打表法适用场景1.3 打表法的优缺点 二、打表法经典案例解析2.1 快速计算斐波那契数列2.1.1 问题描述2.1.2 打表思路2.1.3 Java代码实现2.1.4 复杂度分析 2.2 快速判断质数(埃氏筛法结合打表…

(LeetCode 面试经典 150 题 )121. 买卖股票的最佳时机 (遍历)

题目&#xff1a;121. 买卖股票的最佳时机 思路&#xff1a;遍历&#xff0c;维护已遍历过的元素中的最小值&#xff0c;时间复杂度0(n)。 C版本&#xff1a; class Solution { public:int maxProfit(vector<int>& prices) {int mnprices[0];int mx0;for(int i1;i&…

(洛谷)P4447 [AHOI2018初中组] 分组

题目描述 小可可的学校信息组总共有 n 个队员&#xff0c;每个人都有一个实力值 ai​。现在&#xff0c;一年一度的编程大赛就要到了&#xff0c;小可可的学校获得了若干个参赛名额&#xff0c;教练决定把学校信息组的 n 个队员分成若干个小组去参加这场比赛。 但是每个队员都…

PLA/PHA生物降解化妆品包装材料的稳定性与货架期契合性研究

更多案例&#xff1a;https://npmatc.niicapm.com/ 在可持续发展理念的推动下&#xff0c;化妆品行业正经历一场绿色变革。环保聚合物在包装领域的应用已成为重要趋势&#xff0c;这不仅源于消费者对生态友好产品的需求&#xff0c;更基于全球塑料污染治理的紧迫性。化妆品包装…

STM32[笔记]--4.嵌入式硬件基础

4.嵌入式硬件基础 4.1认识上官二号开发板 主控芯片:STM32F103C8T6高速晶振:8M低速晶振:32.768kLED:5颗KEY:3个 主控芯片内部的资源如下项目介绍内核Cortex-M3Flsah64K*8bitSRAM20K*8bitGPIO37个GPIO,分别为PA0-PB15,PC13-PC15,PD0-PD1ADC2个12bitADC合计12了通道,外部通…

【LLaMA-Factory 实战系列】一、数据准备篇 - 从文本到多模态的完整流程

【LLaMA-Factory 实战系列】一、数据准备篇 - 从文本到多模态的完整流程 1. 引言2. LLaMA-Factory 数据格式概述2.1 Alpaca 格式2.2 ShareGPT 格式 3. 文本数据准备3.1 Alpaca 格式示例3.2 ShareGPT 格式示例3.3 预训练数据格式 4. 多模态数据准备4.1 图像数据准备4.2 视频数据…

JuiceFS 集群部署详细指南:使用 SeaweedFS 作为数据存储,ETCD 作为元数据存储

1. 概述 本指南将详细介绍如何部署一个 JuiceFS 集群,其中数据存储层采用高性能的分布式对象存储 SeaweedFS,元数据存储层采用强一致性的分布式键值存储 ETCD。这种组合方案旨在为用户提供一个高性能、高可用、易于扩展且数据强一致的分布式文件系统解决方案,特别适用于云原…

【数字后端】- 什么是NDR规则?

NDR是指与工艺库的默认规则&#xff08;DR&#xff09;不同的特殊物理规则&#xff1a; 常见的有&#xff1a; 间距规则&#xff08;spacing&#xff09;&#xff1a;增加信号线与邻近线之间的距离&#xff0c;降低Crosstalk串扰。线宽规则&#xff08;width&#xff09;&…

B2B 商城定制的优势:解锁企业数字化转型新动力

精准适配业务流程&#xff0c;贴合企业运营特色​ 每一家企业都有独特的业务流程、运营模式与管理需求。标准化的 B2B 商城往往难以完全满足企业个性化的业务需求&#xff0c;而定制化商城则能够深入剖析企业业务细节&#xff0c;从采购、销售、库存管理到财务管理等全流程&am…

osg实例绘制

#include <osg/Geometry> #include <osg/Geode> #include <osg/Program> #include <osg/VertexAttribDivisor> #include <osgViewer/Viewer> #include <osgViewer/ViewerEventHandlers> #include <random> // 创建单个立方体几何体&…

Qt面试题汇总

目录 1. 简单说一下Qt 2. 用过QT中的哪些模块&#xff1f; 3. 说一些你常用的Qt控件&#xff1f; 4. Qt中如何创建一个窗口&#xff1f; 5. 说一下QT中创建控件的方式? 6. 说一下Qt中信号和槽机制是什么&#xff1f; 7. 说一下QT信号与槽机制原理&#xff1f; 8. 如何自…

【stm32】标准库学习——USART串口

目录 一、USART串口 1.串口参数及时序 2.USART简介 3.配置USART基本结构 4.初始化模板 (1) 接收一个数据 (2) 发送一个数据 一、USART串口 1.串口参数及时序 波特率:串口通信的速率起始位:标志一个数据帧的开始&#xff0c;固定为低电平数据位:数据帧的有效载荷&#…

黑马Day01-03集开始

03集 JSX jsx里面可以写 表达式,表达式里面会返回一个值js语法的扩展,需要babel解析才能够在浏览器运行 语法 使用花括号 {} ,在里面进行编写jsx代码04集 高频场景 使用引号传递字符串 使用js变量 函数调用和方法调用 使用js对象.js自带的一些对象或new出来的对象{&quo…

vue 路由学习

params 不能传递对象类型如 [ ]和{ } query传参 总结&#xff1a; query传参既可以通过name 和path 找到路由规则里的组件&#xff0c;所以为了统一避免非必要麻烦 无论是使用query传参还是 params传参 映射路由建议统一使用 name 进阶 props的使用 备注&#xff1a;资料来自…

JDK安装全攻略:开启Java编程大门

目录 一、安装前准备1.1 确认系统类型1.2 检查系统要求1.3 下载 JDK 安装包 二、Windows 系统下 JDK 安装步骤2.1 双击安装包2.2 选择安装目录2.3 完成安装 三、Windows 系统环境变量配置3.1 打开环境变量设置3.2 配置 JAVA_HOME 变量3.3 配置 Path 变量3.4 验证配置 四、Linux…

《P1253 扶苏的问题》

题目描述 给定一个长度为 n 的序列 a&#xff0c;要求支持如下三个操作&#xff1a; 给定区间 [l,r]&#xff0c;将区间内每个数都修改为 x。给定区间 [l,r]&#xff0c;将区间内每个数都加上 x。给定区间 [l,r]&#xff0c;求区间内的最大值。 输入格式 第一行是两个整数&…

09.【C语言学习笔记】指针(一)

目录 1. 内存和地址 1.1 内存 1.2 究竟该如何理解编址 2. 指针变量和地址 2.1 取地址操作符&#xff08;&&#xff09; 2.2 指针变量和解引用操作符&#xff08;*&#xff09; 2.2.1 指针变量 2.2.2 如何拆解指针类型 2.2.3 解引用操作符 * 2.3 指针变量的大小…

Java中static关键字的作用与使用详解

static是Java中一个非常重要的关键字&#xff0c;它可以用来修饰变量、方法、代码块和嵌套类。下面将从多个方面详细解释static的作用和使用方法。 一、static变量&#xff08;类变量&#xff09; 作用 static变量属于类&#xff0c;而不是类的某个实例。所有实例共享同一个s…

HMLDM-UD100A 型工业激光测距仪通过modbusRTU 转 profinet 网关轻松接入到西门子1200plc

HMLDM-UD100A 型工业激光测距仪通过modbusRTU 转 profinet 网关轻松接入到西门子1200plc 在现代工业生产与自动化控制领域&#xff0c;精准的测量设备与高效的通信技术至关重要。HMLDM-UD100A 型工业激光测距仪凭借其高精度、稳定性强等优势&#xff0c;广泛应用于各类工业场景…

数据结构与算法:图论——深度优先搜索dfs

深度优先搜索dfs 提到深度优先搜索&#xff08;dfs&#xff09;&#xff0c;就不得不说和广度优先搜索&#xff08;bfs&#xff09;有什么区别 根据搜索方式的不同&#xff0c;可以将图的遍历分为「深度优先搜索」和「广度优先搜索」。 深度优先搜索&#xff1a;从某一顶点出…