ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

python-mastery 实战:用 inspect 函数内省自动推导类字段,告别手写 _fields

python-mastery 实战:用 inspect 函数内省自动推导类字段,告别手写 _fields 示例工程教程【免费下载链接】python-masteryAdvanced Python Mastery (course by dabeaz)项目地址https://gitcode.com/gh_mirrors/py/python-mastery点击查看免费下载本篇文章对应 Advanced Python Mastery 课程作者 David BeazleyExercise 6.3 解答主题是如何利用 Python 的函数内省function introspection与inspect模块让结构类Structure自动从__init__()的签名推导出_fields字段元组从而消除此前必须手写_fields (name,shares,price)的重复样板。读完本文你将掌握__code__、inspect.signature()等内省技巧并能独立实现一套签名驱动的轻量数据类基础设施。背景Exercise 6.1 / 6.2 留下的问题在 Exercise 6.1 中我们创建了基类Structure它通过_fields类变量实现通用的__init__()、__repr__()与__setattr__()class Stock(Structure): _fields (name,shares,price)这套方案有两个明显痛点__init__()没有有用的参数签名help(Stock)得不到有意义的信息且不支持关键字参数传参会抛出TypeError: __init__() got an unexpected keyword argument price在 Exercise 6.2 中引入的self._init()栈帧技巧解决了签名问题但类定义变得怪异——用户既要写_fields又要写带self._init()的__init__()重复信息两处维护。Exercise 6.3 练习 的目标就是利用函数内省从__init__()的签名自动生成_fields彻底删除这个手动维护的类变量。核心解法记录在 解答文档对应的完整源码见仓库 Solutions/6_3/structure.py 与 Solutions/6_3/stock.py。一、函数内省基础从dir()到__code__先定义一个普通函数并观察它的属性 def add(x, y): Adds two things return x y dir(add) ... look at the result ...函数对象暴露了大量以双下划线开头的内省属性其中最基本的三个是 add.__name__ add add.__module__ __main__ add.__doc__ Adds two things__name__函数名__module__函数定义所在的模块名__doc__文档字符串。更底层的信息藏在__code__属性中它对应函数的编译后代码对象包含参数个数、局部变量名等实现细节。通过dir(add.__code__)可以进一步查看co_argcount必需参数个数、co_varnames局部变量名元组等字段——这正是从函数内部提取元数据的第一块拼图。二、inspect.signature()获取参数的权威方式直接解析__code__比较原始Python 标准库提供了更高层的inspect模块。对函数调用inspect.signature()即可获得参数签名对象 import inspect sig inspect.signature(add) sig Signature (x, y) sig.parameters mappingproxy(OrderedDict([(x, Parameter x), (y, Parameter y)])) tuple(sig.parameters) (x, y)关键点sig.parameters是一个有序映射mappingproxy包装的OrderedDict键为参数名tuple(sig.parameters)可以直接取出按声明顺序排列的参数名元组该方法同样适用于类对象——inspect.signature(Stock)会解析到其__init__()的签名。三、核心解法用set_fields()从签名推导_fields在 解答文档 给出的structure.py中Structure类由四部分组成# structure.py import sys import inspect class Structure: _fields () staticmethod def _init(): locs sys._getframe(1).f_locals # 获取调用者的局部变量 self locs.pop(self) for name, val in locs.items(): setattr(self, name, val) def __setattr__(self, name, value): if name.startswith(_) or name in self._fields: super().__setattr__(name, value) else: raise AttributeError(No attribute %s % name) def __repr__(self): return %s(%s) % (type(self).__name__, , .join(repr(getattr(self, name)) for name in self._fields)) classmethod def set_fields(cls): sig inspect.signature(cls) cls._fields tuple(sig.parameters)逐块拆解_init()静态方法沿用 Exercise 6.2 的栈帧技巧sys._getframe(1)拿到调用者的栈帧f_locals即__init__()的局部变量字典含self和全部入参pop(self)后逐一setattr到实例上。它被设计为staticmethod因为self是从局部变量里取出来的无需再作为参数传入。__setattr__()白名单限制——属性名以下划线开头私有属性或在_fields之内时放行否则抛出AttributeError(No attribute %s % name)。__repr__()按_fields顺序拼接类名(值, 值, ...)的友好表示。set_fields()类方法本次新增inspect.signature(cls)解析类的__init__()签名tuple(sig.parameters)取出参数名元组直接赋给cls._fields。至此_fields不再需要手动声明。对应的Stock类变得干净很多# stock.py from structure import Structure class Stock(Structure): def __init__(self, name, shares, price): self._init() property def cost(self): return self.shares * self.price def sell(self, nshares): self.shares - nshares Stock.set_fields()在模块底部调用一次Stock.set_fields()类的_fields即被自动填充为(name, shares, price)。用户只需要写一遍__init__()的参数无需再维护_fields。仓库中的完整成品见 Solutions/6_3/stock.py 与 Solutions/6_3/structure.py。四、验证行为与之前完全一致改造后的类行为应与 Exercise 6.1 完全一致 s Stock(nameGOOG, shares100, price490.1) s Stock(GOOG,100,490.1) s.shares 50 s.share 50 Traceback (most recent call last): File stdin, line 1, in module File structure.py, line 12, in __setattr__ raise AttributeError(No attribute %s % name) AttributeError: No attribute share支持关键字参数name、shares、price得益于真实的__init__()签名repr(s)输出Stock(GOOG,100,490.1)属性名白名单生效s.share 50拼写错误被拦截而s._shares 100这类私有属性仍可正常设置。按练习要求再次运行teststock.py单元测试。仓库 Solutions/6_3/teststock.py 覆盖了创建位置参数/关键字参数、cost属性、sell()方法、repr表示以及非法属性赋值等用例。此时仍会有部分测试失败例如test_shares_badtype、test_price_badvalue等涉及类型/取值校验的用例但失败集合应与上一个练习保持一致——这说明本次重构没有引入行为回归只是内部实现从手写_fields换成了签名推导。五、技术要点与下一步本节实现浓缩了三个重要元编程知识点知识点实现载体作用函数对象内省__name__、__module__、__doc__、__code__读取函数名、模块、文档与底层编译信息inspect.signature()sig.parameters以高层 API 获取参数名有序映射可用于函数与类栈帧访问sys._getframe(1).f_locals从调用者帧中批量提取局部变量完成初始化正如解答文档所提示的这套方案仍然有点 hackyself._init()依赖栈帧、set_fields()需要在类定义后手动调用一次。下一站 Exercise 6.4 将转向更优雅的方向——用exec()直接从_fields生成真正的__init__()代码这也是collections.namedtuple的底层原理最终在 Exercise 7.x 通过元类把set_fields()/create_init()这类手动步骤彻底自动化。建议按 练习索引 的顺序依次推进后续练习均建立在本节Structure骨架之上。参考资源Exercise 6.3 解答文档本文主体来源Exercise 6.3 练习说明前置Exercise 6.1Structure初版、Exercise 6.2_init()栈帧技巧完整实现Solutions/6_3/structure.py、Solutions/6_3/stock.py测试用例Solutions/6_3/teststock.py赞分享示例工程教程【免费下载链接】python-masteryAdvanced Python Mastery (course by dabeaz)项目地址https://gitcode.com/gh_mirrors/py/python-mastery点击查看免费下载相关推荐Label Studio 安装部署指南10 分钟搭起文本、图像、音频数据标注环境Label Studio 安装部署指南10 分钟搭起文本、图像、音频数据标注环境 Label Studio 是开源多模态数据标注工具支持文本、图像、音频、视示例工程教程如何掌握Python函数签名inspect模块与参数验证的完整指南如何掌握Python函数签名inspect模块与参数验证的完整指南 Python作为一门动态类型语言在函数参数处理上既灵活又富有挑战性。本文将深入探讨如何使示例工程教程UAE-Large-V1企业级部署方案从原型到生产环境的完整迁移指南UAE Large V1企业级部署方案从原型到生产环境的完整迁移指南 UAE Large V1作为当前最先进的英文句子嵌入模型为企业级AI应用提供了强大的语上一篇Audiveris 八度移位Octave Shift识别与编辑完全指南从记号原理到跨系统逻辑移位的手工建模下一篇如何在Windows上高效安装APK3分钟快速部署Android应用实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表