Python中__new__能否修改__init__接收的foo关键字参数?
__new__能否修改__init__接收的关键字参数?
你的猜想不对,__new__完全可以修改__init__最终接收到的参数值,只是你的代码写法有问题,导致没达到预期效果。
你的代码为什么失效?
你在__new__里递归调用cls.__new__时,虽然传入了foo=float(foo),但Python的对象创建流程是这样的:当__new__返回当前类的实例时,Python会自动用构造函数调用时的原始参数去调用__init__,而不是你在__new__里修改后的参数。你的递归调用最终还是会走到return super().__new__(cls),此时返回实例后,__init__拿到的依然是最初调用Wibble(use_floats_only=True)时的参数——也就是默认的foo=42(int类型),因为你没在原始调用里传foo,所以__init__用的是默认值。
正确的实现方式
有两种可靠的方法让__new__修改__init__的参数:
方法1:手动调用__init__并传入修改后的参数
直接在__new__里创建实例后,手动调用__init__并传递修改后的参数,这样就能覆盖默认的__init__调用:
import sys class Wibble(object): def __new__(cls, *args, foo=42, **kwargs): use_floats = kwargs.pop('use_floats_only', False) instance = super().__new__(cls) if use_floats: print('Warning: using floats only!', file=sys.stderr) # 修改foo为浮点型后,手动调用__init__ instance.__init__(*args, foo=float(foo), **kwargs) else: # 正常调用__init__ instance.__init__(*args, foo=foo, **kwargs) return instance def __init__(self, *args, foo=42, **kwargs): self.foo = foo
执行print(type(Wibble(use_floats_only=True).foo))会输出<class 'float'>,符合预期。
方法2:在实例中暂存修改后的值,让__init__读取
在__new__里把修改后的foo存在实例的临时属性中,然后在__init__里优先使用这个临时值:
import sys class Wibble(object): def __new__(cls, *args, foo=42, **kwargs): use_floats = kwargs.pop('use_floats_only', False) instance = super().__new__(cls) if use_floats: print('Warning: using floats only!', file=sys.stderr) # 把修改后的foo存在临时属性里 instance._temp_foo = float(foo) return instance def __init__(self, *args, foo=42, **kwargs): # 优先用临时属性的foo,没有则用传入的默认值 self.foo = getattr(self, '_temp_foo', foo) # 清理临时属性(可选) if hasattr(self, '_temp_foo'): delattr(self, '_temp_foo')
这种方式不需要手动调用__init__,更符合Python的默认流程,同时也能达到修改参数的目的。
核心原理总结
Python构造对象的流程是:
- 调用
类名(...)时,先触发__new__方法,传入的是构造函数的原始参数 - 如果
__new__返回当前类的实例,Python会自动用原始参数调用该实例的__init__ - 若想让__init__拿到修改后的参数,要么手动调用__init__传修改后的参数,要么在__new__里把修改值存在实例上,让__init__读取。
内容的提问来源于stack exchange,提问作者kjo
相关产品推荐
相关产品推荐

