python:在__init__方法中调用super().__init__是不是太早了?

4 投票
4 回答
6177 浏览
提问于 2025-04-16 16:25

我有一个类的结构,其中在 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 个回答

2

为了让这样的事情顺利进行,你需要设计一个协议,这个协议可以让基础类和派生类之间相互配合,共同完成对象初始化的任务:

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
5

我觉得这个设计不好,你在滥用Python的对象系统。想想其他面向对象的语言,比如C++,你甚至无法控制基类的创建。派生类的构造函数在你的代码运行之前就会调用基类的构造函数。这种行为在良好的类层次结构中几乎总是被期望的,改变它只会带来问题。

当然,你可以做一些补救措施(比如在调用super的构造函数之前先给self.additional_attr赋值,或者其他一些小把戏),但更好的方法是改变你的设计,这样就不需要这些小技巧了。由于你在这里给出了一个抽象的例子,所以很难提供更全面的设计建议。

8

也许你不应该在构造函数里调用 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')

撰写回答