如何从具有__new__方法的超类中正确继承?

Pet*_*erE 9 python inheritance

假设我们有一个类'Parent',由于某种原因已__new__定义,并且继承了它的类'Child'.(在我的情况下,我试图继承我无法修改的第三方课程)

class Parent:
    def __new__(cls, arg):
        # ... something important is done here with arg
Run Code Online (Sandbox Code Playgroud)

我的尝试是:

class Child(Parent):
    def __init__(self, myArg, argForSuperclass):
         Parent.__new__(argForSuperclass)
         self.field = myArg
Run Code Online (Sandbox Code Playgroud)

但同时

p = Parent("argForSuperclass")
Run Code Online (Sandbox Code Playgroud)

按预期工作

c = Child("myArg", "argForSuperclass")
Run Code Online (Sandbox Code Playgroud)

失败,因为'Child'试图调用__new__它从'Parent'而不是它自己的__init__方法继承的方法.

为了获得预期的行为,我必须在"儿童"中进行哪些更改?

bra*_*zzi 11

首先,__new__为了避免这些问题,完全覆盖并不是最佳做法......但我知道,这不是你的错.对于这种情况,覆盖的最佳做法__new__是使其接受可选参数......

class Parent(object):
    def __new__(cls, value, *args, **kwargs):
        print 'my value is', value
        return object.__new__(cls, *args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

......所以孩子们可以得到自己的:

class Child(Parent):
    def __init__(self, for_parent, my_stuff):
        self.my_stuff = my_stuff
Run Code Online (Sandbox Code Playgroud)

然后,它会工作:

>>> c = Child(2, "Child name is Juju")
my value is 2
>>> c.my_stuff
'Child name is Juju'
Run Code Online (Sandbox Code Playgroud)

但是,您父类的作者并不是那么明智并且给了您这个问题:

class Parent(object):
    def __new__(cls, value):
        print 'my value is', value
        return object.__new__(cls)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,只需覆盖__new__子进程,使其接受可选参数,并调用父__new__进程:

class Child(Parent):
    def __new__(cls, value, *args, **kwargs):
        return Parent.__new__(cls, value)
    def __init__(self, for_parent, my_stuff):
        self.my_stuff = my_stuff
Run Code Online (Sandbox Code Playgroud)

  • 子女实际上是在为父母的(编码)罪所承担。 (4认同)

Ben*_*Ben 6

Child 不会调用 Parent's__new__而不是它自己的__init__,而是在 之前调用它自己的__new__(继承自 Parent)。 __init__

您需要了解这些方法的用途以及 Python 何时调用它们。__new__被调用以产生一个实例对象,然后__init__被调用以初始化该实例的属性。Python 会将相同的参数传递给这两种方法:传递给类以开始该过程的参数。

所以当你从一个类继承时你所做的__new__正是你在继承任何其他方法时所做的,这些方法在父级中具有与你想要的不同的签名。您需要覆盖__new__以接收 Child 的参数并Parent.__new__使用它期望的参数进行调用。然后您__init__完全单独覆盖(尽管使用相同的参数列表,并且同样需要__init__使用其自己的预期参数列表调用 Parent 的)。

但是,有两个潜在的困难可能会妨碍您,因为这__new__是自定义获取新实例的过程。它没有理由必须实际创建一个实例(它可以找到一个已经存在的实例),实际上它甚至不必返回类的实例。这可能会导致以下问题:

  1. 如果__new__返回一个现有的对象(比如,因为 Parent 希望能够缓存它的实例),那么你可能会发现你的__init__在已经存在的对象上被调用,如果您的对象应该具有可变状态,这可能非常糟糕。但是,如果 Parent 希望能够做这种事情,它可能会期望对它的使用方式有一堆约束,并以任意打破这些约束的方式对其进行子类化(例如,将可变状态添加到一个类中)期望是不可变的,因此它可以缓存和回收其实例)几乎肯定不会工作,即使您可以正确初始化您的对象。如果发生这种事情,并且没有任何文档告诉您应该如何对 Parent 进行子类化,那么第三方可能不打算让您对 Parent 进行子类化,事情会变得很困难为你。__new__而不是__init__,丑陋的那样。

  2. Python__init__在类的__new__方法实际返回类的实例时才调用该方法。如果 Parent 使用它的__new__方法作为某种“工厂函数”来返回其他类的对象,那么以直接方式进行子类化很可能会失败,除非您需要做的就是改变“工厂”的工作方式。