如何优雅修改子类B的类变量,使其在实例化前生效于__new__的默认参数(继承自第三方无__init__的类A)
如何优雅修改子类B的类变量,使其在实例化前生效于__new__的默认参数(继承自第三方无__init__的类A)
我太懂你这个困扰了——本来想让用户只改个类变量就能切换默认参数,结果发现修改B.bar后,实例化时__new__里的foo还是用的旧值,这都是Python默认参数的绑定机制搞的鬼!
问题根源
你原来的B.__new__里写了foo=bar,这个默认参数是在类定义的时候就绑定了bar当时的值("bar1"),之后你修改B.bar只是更新了类属性,但默认参数的绑定早就完成了,所以实例化时还是会用最初的"bar1"。
最Pythonic的解决方案
我们只需要让__new__在每次实例化时动态获取当前的类变量值,而不是依赖定义时绑定的默认参数。具体做法是给foo设一个哨兵值(比如None),然后在函数内部判断:如果用户没传foo,就取当前类的bar属性。
代码如下:
# 第三方的A类(你无法修改) class A(object): def __new__(cls, foo): obj = super(A, cls).__new__(cls) return obj # 你的子类B class B(A): bar = "bar1" def __new__(cls, foo=None): # 动态获取当前类的bar作为默认值 if foo is None: foo = cls.bar print("actual", foo) rm = super().__new__(cls, foo=foo) return rm
测试一下你想要的用法:
if __name__ == '__main__': B.bar = 'bar2' b = B() # 输出:actual bar2 print(b.bar) # 输出:bar2 # 也支持手动传值覆盖默认 b_custom = B(foo="my_foo") # 输出:actual my_foo
为什么这个方案更好?
- 完全符合你的用户体验需求:用户只需要修改
B.bar就能切换默认参数,代码改动极小 - 不需要用数组这种hack方式,遵循Python的"显式优于隐式"原则
- 上层继承B的子类完全不用修改:它们可以继承B的
bar,也可以自己定义bar类变量,实例化时都会自动取当前类的bar值 - 保留了手动传参覆盖默认的灵活性
额外说明
如果你的子类们需要各自独立的bar值,这个方案也能完美支持——每个子类只需要定义自己的bar类变量就行,__new__会自动获取当前实例所属类的bar,完全不用修改子类的任何方法。
备注:内容来源于stack exchange,提问作者Knyq
相关产品推荐
相关产品推荐

