Python 3.12 Special Attribute - 15 - __kwdefaults__
Python 3.12 Special Attribute -__kwdefaults____kwdefaults__是 Python 中函数对象的一个特殊属性它存储了函数的仅关键字参数keyword-only arguments的默认值以一个字典的形式呈现键为参数名值为默认值。该属性与__defaults__存储位置参数的默认值相对应。通过访问或修改__kwdefaults__你可以在运行时动态调整函数的仅关键字参数的默认行为这对于元编程、装饰器、调试以及动态 API 设计非常有用。本文将详细解析__kwdefaults__的定义、与__defaults__的区别、用途、底层实现并通过多个示例演示其行为。1.__kwdefaults__的基本概念定义__kwdefaults__是一个字典包含函数定义中所有仅关键字参数的默认值。仅关键字参数是指在*args之后或者单独使用*声明的参数它们必须通过关键字传递。适用对象普通函数、方法等。如果函数没有定义任何仅关键字参数则__kwdefaults__为None。可写性__kwdefaults__是可写的允许动态修改或添加仅关键字参数的默认值但修改后会影响函数的后续调用。示例deffunc(a,b10,*,c20,d30):passprint(func.__kwdefaults__)# {c: 20, d: 30}注意b是位置参数其默认值存储在__defaults__中c和d是仅关键字参数其默认值存储在__kwdefaults__中。2.__kwdefaults__与__defaults__的区别属性存储内容类型示例__defaults__位置参数的默认值按顺序元组(10,)__kwdefaults__仅关键字参数的默认值字典{c: 20, d: 30}如果一个函数同时包含两种参数则两者都会存在。示例deffunc(a,b10,*args,c20,d30):passprint(func.__defaults__)# (10,)print(func.__kwdefaults__)# {c: 20, d: 30}3. 仅关键字参数的语法回顾在参数列表中*之后的参数都是仅关键字参数keyword-only。如果函数定义中使用了*args则*args之后的参数也是仅关键字参数。仅关键字参数不能通过位置传递只能通过关键字传递。示例defexample(a,*,b,c10):pass# b 和 c 都是仅关键字参数c 有默认值b 没有默认值必须传递4. 用途与典型场景运行时修改默认值动态调整仅关键字参数的默认值用于测试、热修复或配置变更。装饰器中保留默认值在包装函数时正确传递原函数的仅关键字参数默认值。自省与调试查看函数的仅关键字参数默认值帮助理解函数行为。序列化与反序列化保存函数的默认值字典以便重建函数。动态生成函数在构建新函数时设置其__kwdefaults__以提供默认值。5. 示例与逐行解析示例 1基本访问defconnect(host,port80,*,timeout30,retries3):passprint(connect.__kwdefaults__)# {timeout: 30, retries: 3}print(connect.__defaults__)# (80,)逐行解析行代码解释1-2定义connect函数port是位置参数默认 80timeout和retries是仅关键字参数默认 30 和 3。4打印__kwdefaults__输出字典{timeout: 30, retries: 3}。5打印__defaults__输出元组(80,)。为什么这样写通过__kwdefaults__可以快速查看仅关键字参数的默认值便于文档生成或调试。示例 2动态修改仅关键字参数的默认值defrequest(url,*,methodGET,timeout10):print(fRequest to{url}with method{method}, timeout{timeout})request(https://example.com)# GET, timeout10request.__kwdefaults__[method]POSTrequest(https://example.com)# POST, timeout10request.__kwdefaults__[timeout]30request(https://example.com)# POST, timeout30逐行解析行代码解释1-2定义request仅关键字参数method默认GETtimeout默认10。4第一次调用使用默认值输出GET, timeout10。5修改__kwdefaults__将method的默认值改为POST。6第二次调用输出POST, timeout10。7修改timeout默认值改为30。8第三次调用输出POST, timeout30。为什么这样写展示了动态修改仅关键字参数默认值的能力。这在需要临时改变函数行为例如测试不同配置时非常有用。示例 3在装饰器中保留原函数的默认值importfunctoolsdefwith_logging(func):functools.wraps(func)defwrapper(*args,**kwargs):print(fCalling{func.__name__})# 可以在这里处理默认值无需显式处理因为调用时 kwargs 会包含传递的值returnfunc(*args,**kwargs)# 手动复制默认值属性wrapper.__kwdefaults__getattr(func,__kwdefaults__,None)wrapper.__defaults__getattr(func,__defaults__,None)returnwrapperwith_loggingdefdownload(url,*,retries3,timeout5):print(fDownloading{url}with retries{retries}, timeout{timeout})download(example.com)print(download.__kwdefaults__)# {retries: 3, timeout: 5} (被 wraps 复制)逐行解析functools.wraps会自动将原函数的__kwdefaults__复制到包装函数上因此装饰后的函数仍然保留正确的默认值信息。示例 4为没有仅关键字参数的函数设置__kwdefaults__如果一个函数原本没有仅关键字参数你可以手动给它添加__kwdefaults__属性但这不会影响实际调用因为调用时不会检查该属性除非函数本身支持仅关键字参数。通常这是没有意义的。defsimple(a,b):returnab simple.__kwdefaults__{c:10}# 不会影响调用因为函数没有仅关键字参数print(simple(1,2))# 3为什么这样写说明__kwdefaults__只是一个属性设置它不会改变函数的签名。只有当函数定义中确实存在仅关键字参数时它才会影响默认值。示例 5结合inspect.signature获取完整信息importinspectdefconfig(host,*,port80,sslTrue):passsiginspect.signature(config)print(sig.parameters[port].default)# 80print(sig.parameters[ssl].default)# Trueprint(config.__kwdefaults__)# {port: 80, ssl: True}逐行解析inspect.signature可以提取所有参数信息包括仅关键字参数的默认值。__kwdefaults__是这些信息的底层存储。6. 底层实现机制CPython在 CPython 中函数对象PyFunctionObject的结构如下typedefstruct{PyObject_HEAD PyObject*func_code;// 代码对象PyObject*func_globals;// 全局命名空间PyObject*func_defaults;// 位置参数默认值__defaults__PyObject*func_kwdefaults;// 仅关键字参数默认值__kwdefaults__PyObject*func_closure;// 闭包变量// ...}PyFunctionObject;func_kwdefaults字段存储了默认值字典或NULL。当函数被调用且缺少某个仅关键字参数时Python 会从该字典中查找对应的默认值。可变性字典本身是可变的因此可以修改内部的值或添加新键虽然添加新键不会影响函数调用因为函数签名中并没有这些参数。与__defaults__的关系两者独立存储分别用于位置参数和仅关键字参数。性能访问__kwdefaults__是直接读取func_kwdefaults指针开销极小。修改字典元素也是正常的字典操作。7. 注意事项与陷阱仅适用于仅关键字参数不要期望__kwdefaults__包含位置参数的默认值那些在__defaults__中。修改后会影响所有调用如果修改了__kwdefaults__中的值后续函数调用将使用新值除非调用时显式传递了该参数。字典的可变性可以直接修改__kwdefaults__字典中的条目无需替换整个字典。但添加新键不会影响函数调用因为函数定义中没有对应的参数名。与functools.partial的区别partial创建的新函数不会共享原函数的__kwdefaults__。线程安全在多线程环境中修改__kwdefaults__可能导致竞争条件需谨慎。8. 与其他特殊属性的关系属性作用__code__函数字节码包含参数个数等信息与默认值无关。__defaults__位置参数的默认值元组。__annotations__参数和返回值注解。__signature__可覆盖函数的签名影响默认值的显示。9. 总结特性说明角色存储函数的仅关键字参数默认值类型dict或None访问方式func.__kwdefaults__可写性可写修改字典内容或替换字典底层PyFunctionObject.func_kwdefaults典型用途动态修改默认值、装饰器、自省最佳实践谨慎修改读取时注意可能为None修改后确保与函数签名一致掌握__kwdefaults__可以让你在运行时动态控制仅关键字参数的行为是实现高级元编程和动态配置的重要工具。希望本文能帮助你全面理解这一特殊属性。如果在学习过程中遇到问题欢迎在评论区留言讨论!