68391


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]


CC BY-SA 4.0
2020-07-03T08:53:44.847
68244
2020-07-08T00:16:57.253
2020-07-06T04:02:08.563
2238
22565
question
python python3
Pythonのsuper()の役割


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

クラスPersonの機能を利用しつつ、一部の機能を追加、変更したいときに、Personを継承したクラスを作成します。このとき、継承元の機能を利用する際にsuper()を使います。  
継承元の機能を何も変える必要がなければ、継承先では何もする必要がありません。

[code]

    class JapanesePerson(Person):
        def say_hello(self):
            print("こんにちは")
    
    yamada = JapanesePerson("山田", 20)
    yamada.say_name()
    yamada.say_hello()
    
[/code]

以上のように、コンストラクタを記述しなければ、継承元のコンストラクタが使われます。

[code]

    私の名前は山田です。年齢は20歳です。
    こんにちは
    
[/code]

> super()なし  
>  以下省略  
>  super()なしでも上記のようにすれば同じ用に動作できるのですが、どうしてsuper()が必要なのでしょうか？

super()が必要なのは、先の説明のとおりPersonを継承しているからです。  
Personの機能をそのまま利用するだけなら継承は不要です。「分かりやすさ」の観点ではむしろ使わない方がいい場合があります。

> super()使用しなくても.pは取り除けますか？

say_name()を定義し、その中でPersonのsay_name()を呼び出せばできます。

[code]

    class JapanesePerson():
        def __init__(self, name, age):
            self.p = Person(name, age)
    
        def say_name(self):
            self.p.say_name()
    
[/code]

* * *

【追記】  
「継承を使うか・使わないか」を判断するときの理由(一例です)

  * Personの機能を変更したとき、JapanesePersonでも変更を有効にしたいのならば、継承を使っておいた方が手間が少ないです(JapanesePersonの変更は不要)。
  * Personの機能を変更したとき、JapanesePersonではその変更を有効にしたくないのであれば、継承を使わない方がよいと思います(JapanesePersonに変更前の機能を実装しなければなりません)。
  * JapanesePersonやPersonが今後変化しないのであれば、「継承を使うか・使わないか」はどちらでもよいと思います。


CC BY-SA 4.0
2020-07-04T13:18:39.547
68273
2020-07-05T07:46:12.067
2020-07-05T07:46:12.067
35558
35558
68244
answer


Just

[code]

    class JapanesePerson(Person):
        def say_hello(self):
            print("こんにちは")
    
[/code]

If you don't write a new `__init__`, it will automatically uses the old one.

Or

[code]

    class JapanesePerson(Person):
        def __init__(self, name, age):
            Person.__init__(self, name, age)
    
[/code]

If you would do something else in `__init__`.

Using super() is usually the best practice.


CC BY-SA 4.0
2020-07-04T14:34:55.480
68274
2020-07-05T02:51:27.703
2020-07-05T02:51:27.703
40928
40928
68244
answer


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

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

[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]

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


CC BY-SA 4.0
2020-07-05T11:58:51.393
68303
2020-07-06T04:06:37.170
2020-07-06T04:06:37.170
12274
12274
68244
answer


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

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]

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


CC BY-SA 4.0
2020-07-08T00:16:57.253
68391
2020-07-08T00:16:57.253
36010
68244
answer