Python: 如何使用 super() 初始化 2 个超类?

Python: how to initialize 2 superclasses using super()?

我有两个超级class父亲和母亲,他们将由Child继承。

class Father:
  def __init__(self, **kwargs):
    self.fathername = kwargs["ffn"] + " " + kwargs["fln"]
    self.fatherage = kwargs["fa"]

class Mother:
  def __init__(self, **kwargs):
    self.mothername = kwargs["mfn"] + " " + kwargs["mln"]
    self.motherage = kwargs["ma"]

这里classChild继承父母

class Child(Father, Mother):
  def __init__(self, **kwargs):
    self.name = kwargs["name"] + " " + kwargs["lastname"]
    self.age = kwargs["age"]

如果我分别调用他们的 __init__(),我可以初始化父亲和母亲。

    Father.__init__(self, **kwargs)
    Mother.__init__(self, **kwargs)

但是如何使用 super() 实现同样的效果?如果我像下面这样调用它,它只会初始化父亲而不是母亲(因为父亲是我假设的 MRO 中的下一个)

    super().__init__(**kwargs)

下面只是覆盖了 __str__() 以显示分配的内容。

def __str__(self):
    return \
    "Im {}, {} years old".format(self.name, self.age) + "\n" + \
    "My dad is {} and he is {} years old".format(self.fathername, self.fatherage) + "\n" + \
    "My mom is {} and she is {} years old".format(self.mothername, self.motherage)

familyname = "Simpson"
child = Child(**{"name": "Bart", "lastname": familyname, "age": 15, "ffn": "Hommer", "fln": familyname, "fa": 54, "mfn": "Marggie", "mln": familyname, "ma": 46})

当我尝试打印 object 时,它会失败,因为 Mother superclass 从未初始化(当我在 Child [=20] 中使用 super() =])

print(child)

The program raises a runtime error

Traceback (most recent call last):
  File "/tmp/pyadv.py", line 225, in <module>
    print(child)
  File "/tmp/pyadv.py", line 217, in __str__
    return     "Im {}, {} years old".format(self.name, self.age) + "\n" +     "My dad is {} and he is {} years old".format(self.fathername, self.fatherage) + "\n" +     "My mom is {} and she is {} years old".format(self.mothername, self.motherage)
AttributeError: 'Child' object has no attribute 'mothername'

那么,如何使用super来初始化两个superclasses呢?

编辑: 我试图将 super(Father, self).__init__(**kwargs)super(Mother, self).__init__(**kwargs) 添加到 superclasses __init__() 方法中,但出现以下错误:

Traceback (most recent call last):
  File "/tmp/pyadv.py", line 225, in <module>
    child = Child(**{"name": "Bart", "lastname": familyname, "age": 15, "ffn": "Hommer", "fln": familyname, "fa": 54, "mfn": "Marggie", "mln": familyname, "ma": 46})
  File "/tmp/pyadv.py", line 217, in __init__
    super().__init__(**kwargs)
  File "/tmp/pyadv.py", line 199, in __init__
    super(Father, self).__init__(**kwargs)
  File "/tmp/pyadv.py", line 208, in __init__
    super(Mother, self).__init__(**kwargs)
TypeError: object.__init__() takes no parameters

我还尝试将 super(Father, self).__init__()super(Mother, self).__init__()__init__() 中没有参数)添加到 superclasses __init__() 方法中,但后来我得到了以下错误:

Traceback (most recent call last):
  File "/tmp/pyadv.py", line 225, in <module>
    child = Child(**{"name": "Bart", "lastname": familyname, "age": 15, "ffn": "Hommer", "fln": familyname, "fa": 54, "mfn": "Marggie", "mln": familyname, "ma": 46})
  File "/tmp/pyadv.py", line 217, in __init__
    super().__init__(**kwargs)
  File "/tmp/pyadv.py", line 199, in __init__
    super(Father, self).__init__()
  File "/tmp/pyadv.py", line 206, in __init__
    self.mothername = kwargs["mfn"] + " " + kwargs["mln"]
KeyError: 'mfn'

解决方案一: @blkkngt strip-off 下面

方案二: 根超级class,详细here

class Root:
  def __init__(self, **kwargs):
    pass

class Father(Root):
  def __init__(self, **kwargs):
    self.fathername = kwargs["ffn"] + " " + kwargs["fln"]
    self.fatherage = kwargs["fa"]
    super().__init__(**kwargs)

class Mother(Root):
  def __init__(self, **kwargs):
    self.mothername = kwargs["mfn"] + " " + kwargs["mln"]
    self.motherage = kwargs["ma"]
    super().__init__(**kwargs)

class Child(Father, Mother):
  def __init__(self, **kwargs):
    self.name = kwargs["name"] + " " + kwargs["lastname"]
    self.age = kwargs["age"]
    super().__init__(**kwargs)
  def __str__(self):
    return \
    "Im {}, {} years old".format(self.name, self.age) + "\n" + \
    "My dad is {} and he is {} years old".format(self.fathername, self.fatherage) + "\n" + \
    "My mom is {} and she is {} years old".format(self.mothername, self.motherage)

familyname = "Simpson"
child = Child(**{"name": "Bart", "lastname": familyname, "age": 15, "ffn": "Homer", "fln": familyname, "fa": 54, "mfn": "Marge", "mln": familyname, "ma": 46})

print(child)

来自此 link 中接受的答案:

Invocation via super doesn't call all the parents, it calls the next function in the MRO chain. For this to work properly, you need to use super in all of the __init__s

class Parent1(object):
    def __init__(self):
        super(Parent1, self).__init__()
        self.var1 = 1

class Parent2(object):
    def __init__(self):
        super(Parent2, self).__init__()
        self.var2 = 2

class Child(Parent1, Parent2):
    def __init__(self):
        super(Child, self).__init__()

Python中的多重继承需要协同。也就是说,两个 parent class 需要知道彼此存在的可能性(尽管他们不需要知道彼此的任何详细信息)。那么哪个parent先被命名就可以调用另一个parent的__init__方法。 super就是这样工作的,它总是调用正在操作的实例的MRO(方法解析顺序)中的下一个class。

您的代码很难正确执行此操作,因为您总是在 super 调用中传递完整的 kwargs 指令。当第二个 parent 试图调用 MRO 中的最后一个 class object 时,这会成为一个问题,它不希望接收任何关键字参数。相反,每个 class 的 __init__ 方法通常应该显式命名它期望的参数,并且在调用 super().__init__ 时不要再次传递它们(除非它知道它的 parent classes 也需要参数)。

试试这个:

class Father:
  def __init__(self, ffn, fln, fa, **kwargs): # name the parameters we expect
    super().__init__(**kwargs)           # pass on any unknown arguments
    self.fathername = ffn + " " + fln    # use parameters by name, rather than via kwargs
    self.fatherage = fa

class Mother:
  def __init__(self, mfn, mln, ma, **kwargs):
    super().__init__(**kwargs)
    self.mothername = mfn + " " + mln
    self.motherage = ma

class Child(Father, Mother):
  def __init__(self, name, lastname, age, **kwargs):
    super().__init__(**kwargs)
    self.name = name " " + lastname
    self.age = age

    def __str__(self):
        return \
        "Im {}, {} years old".format(self.name, self.age) + "\n" + \
        "My dad is {} and he is {} years old".format(self.fathername, self.fatherage) + "\n" + \
        "My mom is {} and she is {} years old".format(self.mothername, self.motherage)


familyname = "Simpson"
child = Child(name="Bart", lastname=familyname, age=15, # you can use keyword syntax here
              ffn="Homer", fln=familyname, fa=54,
              mfn="Marge", mln=familyname, ma=46)
print(child)

注意在Python 3中,你通常不需要传递任何参数给super(),它可以判断出它是从哪个class调用的并自动工作.在 Python 2 中,您必须指定当前的 class,但不再需要了。

最后一点。虽然我确信您的代码只是一个示例,但是 classes 的名称在 OOP 设计方面非常糟糕。继承意味着两个 class 之间的 IS-A 关系,这对人们来说并不是很合适。例如 (Bart) 在示例代码中创建的 child 不是 MotherFather,但代码说他是,因为他是 [=] 的实例20=] 和 Father class 是的。描述与 parent 的人际关系的更好方法是 HAS-A。每个 child 都有一个 母亲和父亲。您可以使用封装建立 HAS-A 关系。这意味着,对于属性中的每个 parent,child object 将引用一个 object。有趣的是,这可以只用一个 class 来完成(这可能是你没有学习这个的原因,如果你正在学习继承):

class Person:
    def __init__(self, firstname, lastname, age, father=None, mother=None):
        self.name = firstname + " " + lastname
        self.age = age
        self.father = father  #  set the attributes for our parents here
        self.mother = mother

fn = "Simpson"
bart = Person("Bart", fn, 15, Person("Homer", fn, 54), Person("Marge", fn, 46))