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

发布于 2024-11-03 08:21:20 字数 896 浏览 0 评论 0原文

我有一个类层次结构,其中 class Base 中的 __init__ 执行一些预初始化,然后调用方法 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__ 方法的末尾,因为它所做的一些工作必须在处理 之前进行附加属性。

该怎么办?

I have a class hierarchy where __init__ in class Base performs some pre-initialization and then calls method calculate. The calculate method is defined in class Base, but it's expected to be redefined in derived classes. The redefined calculate will use some of the attributes that are only available in 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

This is not going to work because calculate method in class Derived will be invoked before self.additional_attr is assigned.

I can't move super().__init__(args) call to the end of the __init__ method because some of the work it does has to happen before processing additional_attr.

What to do?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

拥有 2024-11-10 08:21:20

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

Perhaps you shouldn't have the calculate() call in your constructor then. If you can't construct a derived object by allowing the base constructor to complete first, then you must be doing something wrong IMHO. A sensible approach would be to move that call out of the constructor and perhaps create a factory method to make that call automatically. Then use that method if you need precalculated instances.

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')
°如果伤别离去 2024-11-10 08:21:20

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

当然,您可以进行一些修补(例如在调用 super 的构造函数之前分配 self.additional_attr 或其他技巧),但更好的方法是更改您的设计,这样就不需要这样的黑客攻击。由于您在这里提供了一个抽象的示例,因此很难提供更全面的设计建议。

This is bad design, IMHO, and you're obusing the object system of Python. Consider that in other OO languages like C++, you don't even have control over the creation of base classes. The derived class's constructor calls the base constructor before your code runs. Such behavior is almost always expected of well-behaved class hierarchies, and changing it can only lead to problems.

Sure, you can do some patching (such as assigning self.additional_attr before the call to super's constructor, or other tricks), but the better way would be to change your design so that it won't require such hacks. Since you've presented an abstract example here, it's hard to give more comprehensive design advice.

笑着哭最痛 2024-11-10 08:21:20

为了让这样的事情发挥作用,您需要设计一个协议,允许基类和派生类相互协作来完成对象初始化任务:

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

In order for something like this to work, you need to design a protocol that allows the base and derived class(es) to cooperate with each other to accomplish the object initialization task:

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
或十年 2024-11-10 08:21:20

您可以将 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

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

Can you pass the additional_attr as a parameter to __init__ method of base class and propogate it from there to calculate method?

Say something like:

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

This is roundabout way, but with no information about what the pre-initialisation steps are, it is hard to say whether the above approach would help you.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文