当前位置: 首页 > article >正文

Python 3.12 MagicMethods - 77 - __getattr__

Python 3.12 Magic Method -__getattr__(self, name)__getattr__是 Python 中用于定义属性访问失败时的行为的核心魔术方法。当尝试访问一个对象的属性而正常的属性查找机制实例字典、类字典、父类等都找不到该属性时Python 会调用此方法。它为动态属性、懒加载、代理模式等高级功能提供了基础。本文将详细解析其定义、底层机制、设计原则并通过多个示例逐行演示如何正确实现。1. 定义与签名def__getattr__(self,name)-object:...参数self当前对象实例。name要访问的属性名字符串。返回值应返回属性值或抛出AttributeError如果无法处理该属性。调用时机当通过常规方式如obj.name访问属性且该属性不存在时Python 会在抛出AttributeError之前调用此方法。2. 属性查找顺序与__getattr__的位置Python 的属性访问遵循一套清晰的查找顺序理解这个顺序是掌握__getattr__的关键首先调用__getattribute__如果存在这是无条件的属性访问拦截器。在__getattribute__内部Python 会按照实例__dict__→ 类字典 → 基类字典 → 描述符协议 →__getattr__的顺序查找属性。如果所有常规查找都失败并且类定义了__getattr__则调用它并将其返回值作为属性值。如果__getattr__也未定义或抛出AttributeError则最终抛出AttributeError。因此__getattr__是属性查找的最后一道防线仅在属性确实不存在时被触发。与__getattribute__的区别__getattribute__无条件调用拦截所有属性访问无论属性是否存在。__getattr__有条件调用仅在常规查找失败后调用。通常我们应优先考虑__getattr__因为它更安全不会干扰正常属性访问。__getattribute__非常强大但也容易导致无限递归需要谨慎使用。3. 底层实现机制在 CPython 中属性访问由PyObject_GetAttr和PyObject_GetAttrString函数处理。其核心流程简化如下调用对象的类型对象的tp_getattro或tp_getattr槽位对应 Python 层的__getattribute__。在默认的__getattribute__实现中会依次查找实例字典、类字典、基类、描述符。如果所有查找都失败且类型对象中定义了__getattr__方法存储在tp_dict中则调用它。如果__getattr__也不存在则抛出AttributeError。因此__getattr__是在 C 层面实现的作为最后的后备机制。4. 设计原则与最佳实践仅用于处理缺失属性不要用__getattr__来实现已有的属性这会导致混乱和性能下降。避免无限递归在__getattr__内部不要直接使用self.name访问属性否则会再次触发__getattr__导致无限递归。应使用object.__getattribute__(self, name)或super().__getattribute__(name)来访问真实属性。抛出合适的异常如果无法处理请求的属性应抛出AttributeError而不是返回None或其他占位符除非语义允许。与__setattr__、__delattr__配合如果动态设置了属性应确保__setattr__能正确处理避免不一致。文档化明确说明哪些属性是动态生成的避免用户困惑。5. 示例与逐行解析示例 1简单的动态属性懒加载classLazyObject:def__init__(self):self._data{}def__getattr__(self,name):# 当访问不存在的属性时自动创建一个对应的数据项print(f__getattr__ called for {name})ifname.startswith(_):raiseAttributeError(f{type(self).__name__} object has no attribute {name})# 动态生成属性值valuefdynamic value for{name}self._data[name]valuereturnvaluedef__setattr__(self,name,value):# 如果属性以下划线开头按正常方式设置ifname.startswith(_):super().__setattr__(name,value)else:self._data[name]valuedef__delattr__(self,name):ifnameinself._data:delself._data[name]else:super().__delattr__(name)逐行解析行代码解释1-3__init__初始化内部字典_data用于存储动态生成的属性。注意使用下划线前缀避免与动态属性冲突。4-11__getattr__当访问不存在的属性时调用。5打印调试信息仅用于演示。6-7检查下划线属性如果属性名以下划线开头我们不想动态生成而是抛出AttributeError让调用者知道该属性不存在。这可以防止误拦截私有属性。8-10动态生成生成一个值存入_data然后返回。这样下次访问时属性已存在于_data不再触发__getattr__。11返回值返回动态生成的值。12-15__setattr__控制属性赋值。如果属性名以下划线开头使用父类的__setattr__正常设置例如_data。否则将值存入_data。16-19__delattr__如果属性在_data中删除否则调用父类方法处理内置属性。为什么这样写使用内部字典_data存储动态属性避免了与实例的__dict__冲突。在__getattr__中检查下划线属性避免意外处理私有属性。配合__setattr__和__delattr__使对象行为一致。验证objLazyObject()print(obj.foo)# 第一次访问触发 __getattr__print(obj.foo)# 第二次访问属性已存在仍会触发 __getattr__obj.barexplicit# 通过 __setattr__ 显式设置print(obj.bar)# 返回的不是显示设置的值哦这是为什么呢运行结果__getattr__ called for foo dynamic value for foo __getattr__ called for foo dynamic value for foo __getattr__ called for bar dynamic value for bar解析并没有将属性foo, bar等属性添加到实例的__dict__。示例 2代理模式动态转发classProxy:def__init__(self,target):self._targettargetdef__getattr__(self,name):# 将属性访问转发给目标对象print(fProxying attribute {name} to{self._target})returngetattr(self._target,name)def__setattr__(self,name,value):ifname_target:super().__setattr__(name,value)else:setattr(self._target,name,value)def__delattr__(self,name):ifname_target:super().__delattr__(name)else:delattr(self._target,name)classReal:def__init__(self):self.value42defmethod(self):returnHello逐行解析行代码解释1-3__init__保存目标对象到私有属性_target。4-7__getattr__将所有未定义的属性访问转发给目标对象。使用getattr(self._target, name)。8-13__setattr__特殊处理_target属性避免递归其余属性赋值转发给目标对象。14-19__delattr__类似地处理删除。20-25测试代理对象透明地转发属性和方法调用。为什么这样写使用私有属性_target存储目标对象并在__setattr__中特殊处理避免循环调用。通过转发代理对象看起来就像目标对象本身实现了透明代理。验证realReal()proxyProxy(real)print(proxy.value)# 42 (通过 __getattr__ 转发)print(proxy.method())# Hello (转发方法调用)proxy.new_attr100# 通过 __setattr__ 转发print(real.new_attr)# 100运行结果Proxying attribute value to __main__.Real object at 0x000001F1B75F5CA0 42 Proxying attribute method to __main__.Real object at 0x000001F1B75F5CA0 Hello 100示例 3懒加载属性计算属性importmathclassCircle:def__init__(self,radius):self.radiusradius self._areaNonepropertydefarea(self):ifself._areaisNone:self._areamath.pi*self.radius**2returnself._areadef__getattr__(self,name):ifnamediameter:return2*self.radiusraiseAttributeError(f{type(self).__name__} object has no attribute {name})解析这里__getattr__用于动态生成直径而area通过property实现懒加载缓存。__getattr__只处理未定义的属性。验证cCircle(5)print(c.area)# 计算并缓存面积print(c.diameter)# 动态计算直径# print(c.circumference) # AttributeError运行结果78.53981633974483 10示例 4避免递归陷阱classBad:def__getattr__(self,name):returnself.name# 错误会无限递归classGood:def__getattr__(self,name):returnobject.__getattribute__(self,name)# 正确方式但通常不会这么用def__getattr__(self,name):returnsuper().__getattribute__(name)# 另一种正确方式# 正确的使用方式在 __getattr__ 中不要直接访问 self.name解析在__getattr__内部如果试图通过self.name访问属性会再次触发__getattr__因为属性可能不存在导致无限递归。应使用object.__getattribute__(self, name)或super().__getattribute__(name)来绕过__getattr__直接调用基类的属性访问方法。验证bBad()print(b.anything)运行结果RecursionError: maximum recursion depth exceeded6. 注意事项与陷阱不要与__getattribute__混淆__getattribute__拦截所有访问而__getattr__只拦截缺失的属性。除非必要否则优先使用__getattr__。无限递归在__getattr__内部绝对不要使用self.name或getattr(self, name)应使用object.__getattribute__(self, name)。与__setattr__的协同如果动态生成了属性应确保__setattr__也能正确处理否则会出现不一致。不要滥用__getattr__会让代码变得隐晦增加调试难度。只有在确实需要动态属性时才使用。性能影响每次访问缺失属性都会调用__getattr__可能比正常属性慢。如果频繁访问应考虑缓存结果。7. 总结特性说明角色定义属性访问失败时的后备行为签名__getattr__(self, name) - object调用时机常规属性查找失败后底层由tp_getattro在属性查找失败时调用与__getattribute__的区别__getattribute__无条件拦截__getattr__仅当属性缺失时最佳实践避免递归、与__setattr__协同、仅用于动态属性掌握__getattr__是深入理解 Python 属性机制的关键。它为实现动态代理、懒加载、ORM 中的字段访问等高级功能提供了优雅的解决方案。通过正确使用可以让代码更灵活、更强大。如果在学习过程中遇到问题欢迎在评论区留言讨论!

相关文章:

Python 3.12 MagicMethods - 77 - __getattr__

Python 3.12 Magic Method - __getattr__(self, name)__getattr__ 是 Python 中用于定义 属性访问失败时的行为 的核心魔术方法。当尝试访问一个对象的属性,而正常的属性查找机制(实例字典、类字典、父类等)都找不到该属性时,Pyt…...

AIGlasses OS Pro内网穿透方案:安全远程访问智能视觉系统

AIGlasses OS Pro内网穿透方案:安全远程访问智能视觉系统 1. 引言 想象一下这样的场景:你负责的智能视觉系统部署在企业内部网络中,突然接到紧急通知需要远程调试设备。传统方案需要你亲自到现场,或者搭建复杂的VPN网络&#xf…...

INA199电流检测芯片在便携式储能箱中的实战应用(附电路设计图)

INA199电流检测芯片在便携式储能箱中的实战应用(附电路设计图) 便携式储能设备正成为户外活动、应急供电等场景的核心装备,而精准的电流检测能力直接决定了产品的安全性和用户体验。作为硬件工程师,我们常常需要在有限的空间和功耗…...

Ymodem vs Xmodem:老牌文件传输协议对比及现代应用场景分析

Ymodem与Xmodem协议深度解析:从技术原理到现代IoT应用实战 在嵌入式系统开发和工业设备维护中,文件传输协议的选择往往决定了系统升级效率和可靠性。当我们需要为资源受限的设备设计固件更新方案时,Ymodem和Xmodem这两个"老将"依然…...

从SLC到QLC:NAND Flash技术演进对消费电子的影响(含选购指南)

从SLC到QLC:NAND Flash技术演进如何重塑你的电子设备体验 每次打开手机相册或启动笔记本电脑时,你可能不会想到,这些流畅体验的背后是一场持续了二十多年的存储技术革命。NAND Flash芯片——这个藏在所有智能设备内部的"记忆体"&am…...

newklio-library-esp:ESP8266/ESP32专用云连接中间件

1. 项目概述newklio-library-esp是一个面向 ESPRESSIF 系统级芯片(SoC)平台的轻量级云连接中间件库,专为将 ESP8266 及兼容 ESP32 系列设备接入 NewKlio 物联网云平台而设计。该库不依赖完整操作系统栈,可运行于裸机(B…...

Flink本地WEB-UI的隐藏玩法:不装集群也能实时监控任务状态(IDEA/Eclipse通用)

Flink本地WEB-UI的隐藏玩法:不装集群也能实时监控任务状态(IDEA/Eclipse通用) 在分布式计算领域,Flink以其卓越的流处理能力著称,但许多开发者可能不知道,即使在没有部署完整集群的情况下,我们依…...

假设检验避坑指南:t检验、ANOVA和卡方检验的常见误用场景解析

假设检验避坑指南:t检验、ANOVA和卡方检验的常见误用场景解析 在数据分析的实践中,假设检验是验证研究结论可靠性的重要工具。然而,即使是经验丰富的研究者,也常常陷入统计检验的误区。本文将深入剖析t检验、ANOVA和卡方检验三大常…...

YOLOv12优化升级:官方镜像训练更稳定,内存占用显著降低

YOLOv12优化升级:官方镜像训练更稳定,内存占用显著降低 1. YOLOv12核心架构革新 YOLOv12标志着目标检测领域的一次重大技术跃迁。与以往版本最大的不同在于,它彻底摒弃了传统CNN架构,转而采用以注意力机制为核心的创新设计。这种…...

大多数人以为AI Agent必须“铁板一块”才能可靠,但我用OpenClaw后发现:它全靠一堆MD文件纸糊运行,却每天正常运转——这和人类文明的秘密一模一样!

大多数人以为,要让AI真正干活,必须像传统软件一样,代码严丝合缝、逻辑铁板一块、确定性拉满。 可我天天泡在OpenClaw和Claude Code里后,彻底傻眼了:这些系统全靠SOUL.md、AGENTS.md、MEMORY.md、SKILL.md这些纯文本文件…...

快速部署次元画室:Ubuntu服务器环境准备与镜像运行实战

快速部署次元画室:Ubuntu服务器环境准备与镜像运行实战 你是不是也想过,要是能有个随时在线、专属自己的AI角色设计师该多好?不用再为小说人物形象发愁,不用再为游戏角色设计绞尽脑汁,只要输入一段描述,就…...

跨平台大数据文本分析解决方案比较

跨平台大数据文本分析解决方案比较 ——从“一把螺丝刀”到“整座工具箱”的全景拆解作者:知识架构师李 目标:让 10 岁孩子知道“文本分析在干什么”,让 10 年老兵知道“该选哪把瑞士军刀”。0. 开场白:当“文字”变成“数据洪水”…...

万物识别模型优化技巧:提升图片识别准确率的3个方法

万物识别模型优化技巧:提升图片识别准确率的3个方法 你是否遇到过这样的情况:满怀期待地将一张精心拍摄的照片丢给AI识别,结果它却给出了一个让人哭笑不得的答案?比如,把一只可爱的橘猫识别成了“一团毛线”&#xff…...

从流水灯到LFSR:Verilog移位寄存器的实战应用

从流水灯到LFSR:Verilog移位寄存器的实战应用 在数字电路设计中,移位寄存器就像一位不知疲倦的搬运工,能够将数据在时钟节拍下有序地移动。这种看似简单的操作,却能衍生出从基础显示控制到高级伪随机数生成的多种应用场景。本文将…...

DIY植物生长箱环境监测系统:STM32+温湿度+气压+CO2一站式解决方案

DIY植物生长箱环境监测系统:STM32温湿度气压CO2一站式解决方案 在家庭种植和实验室研究中,精确控制植物生长环境是提升作物品质的关键。传统温湿度计和CO2检测仪往往功能单一、数据分散,而市面上的专业环境监测设备又价格昂贵。本文将带你用S…...

提示工程代码审查“质量 gates”:7条准则帮你守住底线

提示工程代码审查“质量 Gates”:7条准则帮你守住底线 一、引言:为什么你的代码审查总漏问题? 作为开发工程师,你一定遇到过这样的场景: 张三提交的代码,你审的时候只看了风格,没注意逻辑,结果上线后发现功能不符合需求; 李四的代码,你审的时候觉得“看起来没问题”…...

phyphox远程控制技巧:3种方法实现电脑大屏同步显示手机传感器数据(局域网/WiFi版)

Phyphox远程教学全攻略:3种低延迟方案实现传感器数据大屏同步 物理课堂上,当我们需要演示自由落体、圆周运动或弹簧振动时,传统DIS设备的笨重和昂贵常常让实验效果大打折扣。而如今,每位学生口袋里的智能手机都内置了高精度加速度…...

洞察大数据领域GDPR的最新动态

洞察大数据领域GDPR的最新动态:从合规红线到数据价值的平衡艺术关键词:GDPR、数据隐私、大数据合规、被遗忘权、跨境数据传输、隐私增强技术、AI伦理摘要:在大数据浪潮中,个人数据已成为“数字石油”,但欧盟《通用数据…...

嵌入式开发必备:STM32CubeMX最新版安装与HAL库工程创建实战

STM32CubeMX高效开发指南:从安装到HAL库工程实战 作为一名长期奋战在嵌入式开发一线的工程师,我深刻理解从传统开发模式切换到图形化配置工具时的困惑与期待。记得第一次接触STM32CubeMX时,那种"点点鼠标就能生成代码"的体验彻底颠…...

从零玩转MSP430:用CCS 20.1.1实现库函数开发(附Driverlib配置技巧)

从零玩转MSP430:用CCS 20.1.1实现库函数开发(附Driverlib配置技巧) 在嵌入式开发领域,MSP430系列以其超低功耗和丰富外设资源著称,但很多开发者在从寄存器操作转向库函数开发时常常遇到障碍。本文将基于Code Composer …...

Windows 11/10系统下,手把手教你搞定华为eNSP与VirtualBox 5.2.28的兼容性配置

Windows 11/10系统下华为eNSP与VirtualBox 5.2.28兼容性配置实战指南 当网络工程师或学生在全新Windows 11/10系统上部署华为eNSP时,最令人头疼的莫过于与VirtualBox 5.2.28的兼容性问题。本文将提供一套经过验证的解决方案,帮助您绕过常见的安装陷阱。 …...

告别手忙脚乱!在Unity中为Oculus Quest应用集成系统键盘的保姆级指南(含Manifest文件修改)

告别手忙脚乱!在Unity中为Oculus Quest应用集成系统键盘的保姆级指南(含Manifest文件修改) 在VR应用开发中,文本输入功能往往成为用户体验的关键瓶颈。想象一下,当用户需要输入姓名、搜索内容或发送消息时,…...

分人群建站解决方案:中小企业、创业者、外贸人怎么选对AI建站工具?

同样是“想建个网站”,中小企业主、刚起步的创业者、做外贸生意的卖家,他们心里的“网站”其实千差万别。需求不同,适配的建站方案自然也不同。本文针对几类典型人群,拆解他们的核心痛点,并提供对应的建站思路与工具选…...

S32K3双核开发实战:如何用DTCM优化中断响应速度(附代码)

S32K3双核开发实战:如何用DTCM优化中断响应速度(附代码) 在汽车电子领域,实时性往往是系统设计的核心指标之一。当工程师面对多核MCU的中断处理需求时,如何确保关键中断能在最短时间内得到响应,成为影响系…...

S32K144-NXP EB tresos工程配置实战:从零搭建Autosar开发环境

1. 环境准备与工具安装 在开始S32K144的Autosar开发之前,我们需要先搭建好开发环境。这个环节看似基础,但很多新手开发者容易在这里踩坑。我建议使用NXP官方推荐的开发工具组合:S32 Design Studio EB tresos Studio。这两个工具配合使用&…...

OpenCV颜色查找表LUT的5个高级用法:从图像反转到颜色空间缩减

OpenCV颜色查找表LUT的5个高级用法:从图像反转到颜色空间缩减 在数字图像处理领域,颜色查找表(Look Up Table,简称LUT)是一种高效且强大的工具。它通过预先计算的映射关系,能够实现像素值的快速转换&#x…...

Nanobot多模型集成指南:HuggingFace模型库调用方法

Nanobot多模型集成指南:HuggingFace模型库调用方法 1. 引言 如果你正在使用Nanobot这个轻量级AI助手框架,想要扩展它的能力来支持更多类型的AI任务,那么集成HuggingFace模型库绝对是个不错的选择。HuggingFace提供了数千个预训练模型&#…...

STC15单片机低功耗实战:从模式选择到电路优化

1. STC15单片机低功耗设计的核心价值 搞嵌入式开发的朋友都知道,电池供电设备的续航能力直接决定产品成败。我去年做过一个农业传感器项目,就因为功耗没控制好,客户每两周就得爬梯子换电池,差点被投诉到怀疑人生。STC15系列单片机…...

龙芯99pai开发板网络配置避坑实录:从串口连接到静态IP,新手也能一次点亮

龙芯99pai开发板网络配置避坑实录:从串口连接到静态IP,新手也能一次点亮 第一次接触龙芯99pai开发板时,网络配置往往是新手开发者遇到的第一个拦路虎。从硬件连接到软件配置,从串口调试到静态IP设置,每个环节都可能隐藏…...

相控阵雷达开发避坑指南:数据立方体生成中的5个常见错误与解决方案

相控阵雷达开发避坑指南:数据立方体生成中的5个常见错误与解决方案 在毫米波雷达和合成孔径雷达(SAR)系统的开发过程中,数据立方体的构建质量直接决定了后续信号处理算法的有效性。作为雷达算法工程师,我们常常陷入一种…...