Python 3.12 Special Attribute - 14 - __defaults__

张开发
2026/4/23 7:27:08 15 分钟阅读

分享文章

Python 3.12 Special Attribute - 14 - __defaults__
Python 3.12 Special Attribute -__defaults____defaults__是 Python 中函数对象包括普通函数、方法、lambda 等的一个内置特殊属性。它存储了函数的默认位置参数值以一个元组的形式呈现。通过访问__defaults__可以在运行时获取或修改函数的默认参数值这对于动态编程、装饰器、调试以及元编程非常有用。本文将详细解析__defaults__的定义、与__kwdefaults__的区别、用法、底层实现并通过多个示例演示其行为。1.__defaults__的基本概念定义__defaults__是一个元组包含函数的位置参数的默认值。这些默认值按照函数定义时参数的顺序排列从最左边的具有默认值的参数开始。适用对象普通函数、方法、lambda 函数等。对于没有默认参数或所有参数都没有默认值的函数__defaults__为None。可写性__defaults__是可写的允许动态修改函数的默认参数值。但修改后会影响后续调用。示例deffunc(a,b10,c20):passprint(func.__defaults__)# (10, 20)注意__defaults__不包含没有默认值的参数例如上面的a它只包含从第一个有默认值的参数开始的所有默认值。2.__defaults__与__kwdefaults__的区别属性存储内容类型说明__defaults__位置参数positional的默认值元组按照函数定义顺序仅包含有默认值的参数。__kwdefaults__仅关键字参数keyword-only的默认值字典键为参数名值为默认值。示例deffunc(a,b10,*args,c20,d30):passprint(func.__defaults__)# (10,) 注意b 是位置参数c 和 d 是仅关键字参数print(func.__kwdefaults__)# {c: 20, d: 30}3. 用途与典型场景运行时修改默认值动态调整函数的默认参数用于测试、热修复或配置。装饰器处理在装饰器中获取原始函数的默认值以便在包装函数中正确传递。自省与调试查看函数的默认参数值帮助理解函数行为。序列化与反序列化将函数的默认值保存下来以便后续重建。实现动态代理根据需求替换默认值。4. 示例与逐行解析示例 1基本访问defgreet(name,greetingHello,punctuation!):returnf{greeting},{name}{punctuation}print(greet.__defaults__)# (Hello, !)逐行解析行代码解释1-2定义greet函数参数name无默认值greeting默认Hellopunctuation默认!。4打印__defaults__输出(Hello, !)注意顺序与定义一致。为什么这样写通过__defaults__可以快速知道函数有哪些默认值以及它们的顺序。示例 2动态修改默认值defpower(base,exp2):returnbase**expprint(power(3))# 9power.__defaults__(3,)print(power(3))# 27 (3^3)逐行解析行代码解释1-2定义powerexp默认值为 2。4调用power(3)使用默认指数 2输出 9。5修改__defaults__将默认值元组改为(3,)。6再次调用power(3)指数变为 3输出 27。为什么这样写演示了动态修改默认值的能力。这在需要临时改变函数行为如测试时很有用。示例 3结合inspect.signature获取完整信息importinspectdefprocess(data,moder,encodingutf-8):passsiginspect.signature(process)print(sig.parameters[mode].default)# rprint(process.__defaults__)# (r, utf-8)逐行解析inspect.signature可以获取更详细的参数信息包括默认值。但__defaults__提供了直接的元组访问。示例 4装饰器中保留默认值deflog_call(func):defwrapper(*args,**kwargs):print(fCalling{func.__name__}with defaults:{func.__defaults__})returnfunc(*args,**kwargs)returnwrapperlog_calldefmultiply(x,y2):returnx*y multiply(5)# 输出: Calling multiply with defaults: (2,)逐行解析装饰器log_call可以访问被装饰函数的__defaults__用于日志或调试。示例 5修改默认值后对闭包的影响defmake_adder(addend1):defadder(x):returnxaddendreturnadder addmake_adder()print(add(10))# 11make_adder.__defaults__(100,)add2make_adder()print(add2(10))# 110# 注意已经创建的 add 函数不受影响因为它的闭包捕获的是创建时的 addend 值逐行解析修改外部函数make_adder的__defaults__会影响以后调用make_adder创建的闭包但不会改变已存在的闭包。5. 底层实现机制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_defaults字段存储了默认值元组或NULL。当函数被调用且缺少位置参数时Python 会从func_defaults中按顺序取出值填充。不可变元组虽然__defaults__是可写的但元组本身是不可变的。要修改某个默认值必须替换整个元组。与inspect的关系inspect.signature会读取func_defaults和func_kwdefaults来构建完整的签名信息。性能访问__defaults__是直接读取func_defaults指针开销极小。修改时由于元组不可变通常需要创建新元组。6. 注意事项与陷阱仅包含位置参数的默认值仅关键字参数keyword-only的默认值存储在__kwdefaults__中不要混淆。元组的不可变性不能直接修改元组中的某个元素只能整体替换元组。对已创建的闭包无影响修改外部函数的__defaults__不会改变已创建的内部函数因为闭包捕获的是值而非引用。线程安全在多线程环境中修改__defaults__可能导致竞争条件需谨慎。与functools.partial的区别partial创建的是新函数不会修改原函数的__defaults__。7. 与其他特殊属性的关系属性作用__code__函数字节码包含参数个数等信息与默认值无关。__kwdefaults__仅关键字参数的默认值字典。__annotations__参数和返回值注解与默认值独立。__signature__可覆盖函数的签名影响默认值的显示。8. 总结特性说明角色存储函数的位置参数默认值类型tuple或None访问方式func.__defaults__可写性可写替换整个元组底层PyFunctionObject.func_defaults典型用途动态修改默认值、装饰器、自省最佳实践谨慎修改确保与函数签名兼容读取时注意可能为None掌握__defaults__可以让你在运行时动态控制函数的行为是实现高级元编程和调试的重要工具。希望本文能帮助你全面理解这一特殊属性。如果在学习过程中遇到问题欢迎在评论区留言讨论!

更多文章