Pythonのsuper()の役割


super()を使わなくても似た機能を実装できるのになぜ使用するのかわかりません。

[code]

    class Person():
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def say_name(self):
            print("私の名前は" + self.name + "です。年齢は" + str(self.age) + "歳です。")
    
    
    class JapanesePerson(Person):
        def __init__(self, name, age):
            super().__init__(name, age)
    
        def say_hello(self):
            print("こんにちは")
    
    
    yamada = JapanesePerson("山田", 20)
    yamada.say_name()
    yamada.say_hello()
    
[/code]

**super()なし**

[code]

    class Person():
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def say_name(self):
            print("私の名前は" + self.name + "です。年齢は" + str(self.age) + "歳です。")
    
    
    class JapanesePerson():
        def __init__(self, name, age):
            self.p = Person(name, age)
    
        def say_hello(self):
            print("こんにちは")
    
    
    yamada = JapanesePerson("山田", 20)
    yamada.p.say_name()
    yamada.say_hello()
    
[/code]

super()なしでも上記のようにすれば同じように動作できるのですが、どうしてsuper()が必要なのでしょうか？  
使用すると`yamada.p.sayname()`の`.p`を取り除けるからでしょうか？もしくはsuper()使用しなくても`.p`は取り除けますか？

**追記**

以下のようにすれば出来ますが、膨大になると大変という事でしょうか？

[code]

    class Person():
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def say_name(self):
            print("私の名前は" + self.name + "です。年齢は" + str(self.age) + "歳です。")
    
    
    
    class JapanesePerson():
        def __init__(self, name, age):
            self.j = Person(name, age)
    
        def canDrinkAlcohol(self):
            return self.j.age >= 20
    
    
    class BritishPerson():
        def __init__(self, name, age):
            self.b = Person(name, age)
    
        def canDrinkAlcohol(self):
            return self.b.age >= 18
    
        def say_name(self):
            print("I am " + self.b.name + ", " + str(self.b.age) + " yeas old.")
    
    yamada = JapanesePerson("山田", 20)
    yamada.j.say_name()
    print(yamada.canDrinkAlcohol())
    
    nick = BritishPerson("Nick", 17)
    nick.say_name()
    print(nick.canDrinkAlcohol())
    
[/code]




継承を使うか使わないか、どちらが簡潔に書けるか、という実務的な話は他の回答者さんたちがされているので、意味論的な話をします。

Pythonもクラスベースの[オブジェクト指向](https://ja.wikipedia.org/wiki/%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91)言語ですので、その観点からの一般論を言うと、

  * クラスの継承(`super`を使う方)は、[Is-a](https://ja.wikipedia.org/wiki/Is-a)の関係を表すことに適しています。
  * 合成(`super`を使わない方)は[Has-a](https://ja.wikipedia.org/wiki/Has-a)の関係を表すことに適しています。

意味的に“JapanesePerson is Person”は成り立ちますが、“JapanesePerson has
Person”は不自然に感じます。継承がIs-aの関係に使われ、合成がHas-
aに使われるということを知っている他の人が質問者さんの合成をつかったコードを見ると違和感を感じてしまいます。

実際にプログラムを実行するのはコンピューターなので、自然だろうが不自然だろうが同じように動作するし、どちらでもいいと思われるかもしれません。しかし、人間にとって自然か不自然かという感覚は結構重要です。不自然に感じるということは、人間が頭の中で考える概念がうまくプログラムで表せていないということを意味するからです。他の人が見たときに分かりにくく感じる、ということが起こります。

また、継承を使うか合成を使うかで`isinstance(yamada,
Person)`の結果が変わります。継承だと`True`、合成だと`False`です。Pythonでのクラスの継承はまさにIs-aの関係を持っているのです。

なお、`yamada.p.say_name()`のように、`JapanesePerson`の中身`p`を露わに知っていないと使えないというのは、[カプセル化](https://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%97%E3%82%BB%E3%83%AB%E5%8C%96)（内部情報の隠蔽）の観点からは好まれません。これを解決する単純な方法は、中身の`p`の`say_name()`を呼び出すような`say_name()`を定義してしまうことです。（Has-
aではないのが不自然なことには目をつぶって）これを敢えて書いてみると、

[code]

    class JapanesePerson():
        def __init__(self, name, age):
            self.p = Person(name, age)
    
        def say_hello(self):
            print("こんにちは")
    
    　　def say_name(self):
            self.p.say_name()  # Personのsay_name()を呼ぶ
    
[/code]

しかし、このような中身のメソッドを呼び出すだけのメソッドを書くのは、メソッドやクラスが増えてくると面倒ですよね。




> 上記のようにすれば同じように動作できるのですが

違う動作を（しかし同じ手続きで）させるときにどうするつもりですか?

[code]

    class Person():
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def say_name(self):
            print("私の名前は" + self.name + "です。年齢は" + str(self.age) + "歳です。")
    
        def canDrinkAlcohol(self):
            raise NotImplementedError
    
    
    class JapanesePerson(Person):
        def __init__(self, name, age):
            super().__init__(name, age)
    
        def canDrinkAlcohol(self):
            return self.age >= 20
    
    
    class BritishPerson(Person):
        def __init__(self, name, age):
            super().__init__(name, age)
    
        def canDrinkAlcohol(self):
            return self.age >= 18
    
        def say_name(self):
            print("I am " + self.name + ", " + str(self.age) + " yeas old.")
    
    yamada = JapanesePerson("山田", 20)
    yamada.say_name()
    print(yamada.canDrinkAlcohol())
    
    nick = BritishPerson("Nick", 17)
    nick.say_name()
    print(nick.canDrinkAlcohol())
    
[/code]

[code]

    私の名前は山田です。年齢は20歳です。
    True
    I am Nick, 17 yeas old.
    False
    
[/code]

をどう考えますか?

* * *

(追記)

（しかし同じ手続きで） を無視していますね。  
"同じ手続きであること"をメリットとして見ていないわけです。

[code]

    persons = [JapanesePerson("山田", 20), JapanesePerson("佐藤", 19), BritishPerson("Nick", 17)]
    
    for p in persons:
        if p.canDrinkAlcohol():
            p.say_name()
    
[/code]

とか

[code]

    import random
    persons = [JapanesePerson("山田", 20), JapanesePerson("佐藤", 19), BritishPerson("Nick", 17)]
    
    a_person = random.choice(persons)
    
    if not a_person.canDrinkAlcohol():
        a_person.say_name()
    
[/code]

とかいったような使い方のコードを想定していないのかと。  
継承を使うべきか否かという問題はまた別にありますが、同じ機能は同じように呼び出せる方がいいというメリットを **わざわざ** 捨てる必要はないと思います。

