python:在__init__方法中过早地调用super().__ init__?

时间:2011-04-26 05:46:22

标签: python class design-patterns

我有一个类层次结构,其中__init__ class Base执行一些预初始化,然后调用方法calculatecalculate方法在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

这不起作用,因为calculate中的class Derived方法将在分配self.additional_attr之前调用。

我无法将super().__init__(args)调用移至__init__方法的末尾,因为它必须在处理additional_attr之前完成一些工作。

怎么办?

4 个答案:

答案 0 :(得分:7)

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

答案 1 :(得分:4)

这是糟糕的设计,恕我直言,你正在使用Python的对象系统。考虑到在其他OO语言(如C ++)中,您甚至无法控制基类的创建。派生类的构造函数在代码运行之前调用基础构造函数。这种行为几乎总是需要表现良好的类层次结构,而改变它只会导致问题。

当然,您可以进行一些修补(例如在调用self.additional_attr的构造函数或其他技巧之前分配super),但更好的方法是更改​​您的设计以便它不会要求这样的黑客攻击。既然你已经在这里提出了一个抽象的例子,那么很难给出更全面的设计建议。

答案 2 :(得分: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

答案 3 :(得分:1)

您可以将additional_attr作为参数传递给基类的__init__方法,并将其从那里传播到calculate方法吗?

说出类似的话:

class Base(object): 
    def __init__(self, args,additional_attr): 
        print 'Args for base class:%s' %(args)
        self.calculate(additional_attr)

class Derived(Base):
    def __init__(self, args, additional_attr):
        super(Derived,self).__init__(args,additional_attr)

     def calculate(self,val):
         print 'Arg for calculate:%s' %(val)
         self.additional_attr = val
>>> d = Derived(['test','name'],100)
Args for base class:['test', 'name']
Arg for calculate:100

这是迂回的方式,但由于没有关于预初始化步骤的信息,很难说上述方法是否会对您有所帮助。