python:在__init__方法中调用super().__init__是不是太早了?
我有一个类的结构,其中在 class Base
的 __init__
方法里,先做了一些初始化工作,然后调用了 calculate
方法。这个 calculate
方法是在 class Base
中定义的,但它应该在子类中重新定义。重新定义的 calculate
方法会使用一些只有在 class Derived
中才有的属性:
class Base:
def __init__(self, args):
# perform some pre-initialization
...
# now call method "calculate"
self.calculate()
class Derived(Base):
def __init__(self, args, additional_attr):
super().__init__(args)
# do some work and create new instance attributes
...
self.additional_attr = additional_attr
这样做是行不通的,因为在 class Derived
中的 calculate
方法会在 self.additional_attr
被赋值之前就被调用了。
我不能把 super().__init__(args)
的调用放到 __init__
方法的最后,因为它做的一些工作必须在处理 additional_attr
之前完成。
那该怎么办呢?
4 个回答
为了让这样的事情顺利进行,你需要设计一个协议,这个协议可以让基础类和派生类之间相互配合,共同完成对象初始化的任务:
class Base:
def __init__(self, args, *additional_args):
# perform some pre-initialization
# ...
# perform any futher initialization needed by derived classes
self.subclass_setup(*additional_args)
# now call method "calculate"
self.calculate()
def subclass_setup(self, *args):
pass
class Derived(Base):
def __init__(self, args, additional_attr):
super().__init__(args, additional_attr)
def subclass_setup(self, additional_attr):
# do some work and create new instance attributes
# ...
self.additional_attr = additional_attr
我觉得这个设计不好,你在滥用Python的对象系统。想想其他面向对象的语言,比如C++,你甚至无法控制基类的创建。派生类的构造函数在你的代码运行之前就会调用基类的构造函数。这种行为在良好的类层次结构中几乎总是被期望的,改变它只会带来问题。
当然,你可以做一些补救措施(比如在调用super
的构造函数之前先给self.additional_attr
赋值,或者其他一些小把戏),但更好的方法是改变你的设计,这样就不需要这些小技巧了。由于你在这里给出了一个抽象的例子,所以很难提供更全面的设计建议。
也许你不应该在构造函数里调用 calculate()
。如果你不能在基类的构造函数完成后再构造派生对象,那我觉得你可能做错了什么。一个合理的做法是把这个调用移出构造函数,可能可以创建一个工厂方法来自动进行这个调用。如果你需要预先计算好的实例,就使用这个方法。
class Base(object):
def __init__(self, args):
# perform some initialization
pass
def calculate(self):
# do stuff
pass
@classmethod
def precalculated(cls, args):
# construct first
newBase = cls(args)
# now call method "calculate"
newBase.calculate()
return newBase
class Derived(Base):
def __init__(self, args, additional_attr):
super(Derived, self).__init__(args)
# do some work and create new instance attributes
self.additional_attr = additional_attr
@classmethod
def precalculated(cls, args, additional_attr): # also if you want
newDerived = cls(args, additional_attr)
newDerived.calculate()
return newDerived
newBase = Base('foo')
precalculatedBase = Base.precalculated('foo')
newDerived = Derived('foo', 'bar')
precalculatedDerived = Derived.precalculated('foo', 'bar')