__init __()は親クラスの__init __()を呼び出す必要がありますか?


132

私はObjective-Cでこの構文を使用していることに慣れています。

- (void)init {
    if (self = [super init]) {
        // init class
    }
    return self;
}

Pythonは、親クラスの実装も呼び出す必要があります__init__か?

class NewClass(SomeOtherClass):
    def __init__(self):
        SomeOtherClass.__init__(self)
        # init class

これはまたのための真/偽のです__new__()__del__()

編集:非常によく似た質問があります:Pythonでの継承とオーバーライド__init__


コードを大幅に変更しました。オリジナルobjectはタイプミスだったと思います。しかし今、あなたはsuperあなたの質問のタイトルが参照していることすらありません。
SilentGhost 2009

superは親クラスの名前として使用されていると思いました。誰もがその機能について考えているとは思わなかった。誤解があってすみません。
GeorgSchölly09年

回答:


67

Pythonでは、スーパークラスの呼び出し__init__はオプションです。呼び出す場合、super識別子を使用するかどうか、またはスーパークラスに明示的に名前を付けるかどうかもオプションです。

object.__init__(self)

オブジェクトの場合、スーパーメソッドが空であるため、スーパーメソッドを呼び出す必要はありません。も同じです__del__

一方、では__new__、実際にスーパーメソッドを呼び出し、その戻り値を新しく作成されたオブジェクトとして使用する必要があります-明示的に別のものを返したい場合を除きます。


では、superの実装を呼び出すだけの慣例はありませんか?
GeorgSchölly09年

5
古いスタイルのクラスでは、スーパークラスに実際にinitが定義されている場合(多くの場合は定義されていません)、スーパーinitを呼び出すことができます。したがって、人々は通常、スーパーメソッドを呼び出すことを、原則外ではなく、考えています。
Martin v。Löwis、2009

1
Pythonの構文がのように単純であれば、[super init]より一般的です。ただの投機的な考え。Python 2.xのスーパーコンストラクトは、少し厄介です。
u0b34a0f6ae 2009

例:ここで面白いのようです(そしておそらく矛盾)bytes.com/topic/python/answers/... のinit
mlvljr

「オプション」は、呼び出す必要がないという点で異なりますが、呼び出さない場合、自動的に呼び出されることはありません。
マッケイ2017

140

__init__現在のクラスで行われていることに加えて、スーパーの何かを行う必要がある場合は、__init__,それを自分で呼び出す必要があります。これは自動的に行われないためです。しかし、superから何も__init__,必要ない場合は、呼び出す必要はありません。例:

>>> class C(object):
        def __init__(self):
            self.b = 1


>>> class D(C):
        def __init__(self):
            super().__init__() # in Python 2 use super(D, self).__init__()
            self.a = 1


>>> class E(C):
        def __init__(self):
            self.a = 1


>>> d = D()
>>> d.a
1
>>> d.b  # This works because of the call to super's init
1
>>> e = E()
>>> e.a
1
>>> e.b  # This is going to fail since nothing in E initializes b...
Traceback (most recent call last):
  File "<pyshell#70>", line 1, in <module>
    e.b  # This is going to fail since nothing in E initializes b...
AttributeError: 'E' object has no attribute 'b'

__del__同じ方法です(ただし__del__、ファイナライズに依存することに注意してください-代わりにwithステートメントを使用することを検討してください)。

私はめったに使用__new__. しません__init__.


3
クラスD(C)の定義はそのように修正する必要がありますsuper(D,self).__init__()
eyquem

11
スーパー().__のinit __()だけが、スーパー(D、自己).__のinit __()必要はPython 3でのPython 2で動作します
Jacinda

「スーパーのinitから何かが必要な場合...」-これは、あなた/サブクラスが「何か」を必要とするかどうかではなく、基本クラスが有効であるために何かが必要かどうかというケースではないため、非常に問題の多いステートメントです。基本クラスのインスタンスと正しく動作します。派生クラスの実装者として、基本クラスの内部は知らない/すべきではないものであり、両方を記述したため、または内部が文書化されているために行ったとしても、基本の設計は将来的に変更され、不十分な記述のために壊れる可能性があります派生クラス。したがって、基本クラスが完全に初期化されていることを常に確認してください。
ニック

105

Anonの答え:
__init__現在のクラスで行われていることに加えて、superから何かを行う必要がある場合は、自動的に行われ__init__ないため、自分で呼び出す必要があります。」

それは信じられないことです。彼は相続の原則に正反対の言葉を使っています。


これは、ということではありません「スーパーのから何かが__init__ (...)が自動的に発生しません」基本クラスがあるため、それが自動的に行われますが、それは起こらないということで、__init__派生-CLASの定義によって上書きされます__init__

それで、__init__誰かが継承に頼ったときに何を目的としているのかをオーバーライドするので、なぜ派生クラスを定義するのですか?

これは、基本クラスで行われないことを定義する必要があるためです。それ__init__を取得する唯一の可能性は、その実行を派生クラスの__init__関数に置くことです。
言い換えると、後者がオーバーライドされなかった場合に、基本クラス__init__で自動的に行われることに加え、基本クラスで何かが必要になり__init__ます。
反対ではありません。


次に、問題は、基本クラスに存在する必要な命令__init__がインスタンス化の瞬間にアクティブ化されないことです。この非アクティブ化を相殺するには、特別な何かが必要です。ベースクラスを明示的に呼び出すことで、ベースクラスによって実行される初期化__init__KEEP、NOT TO ADDすることができます__init__。それはまさに公式ドキュメントで述べられていることです:

派生クラスのオーバーライドメソッドは、実際には、同じ名前の基本クラスメソッド単に置き換えるのではなく、拡張したい場合があります。基本クラスのメソッドを直接呼び出す簡単な方法があります:BaseClassName.methodname(self、arguments)を呼び出すだけです。
http://docs.python.org/tutorial/classes.html#inheritance

それだけです。

  • 基本クラスによって実行される初期化、つまり純粋な継承を維持することを目的とする場合、特別なことは何も必要ありません。__init__派生クラスで関数を定義することは避けなければなりません。

  • 目的が基本クラスによって実行される初期化を置き換えることである場合、__init__派生クラスで定義する必要があります

  • 基本クラスによって実行される初期化にプロセスを追加することを目的とする場合、基本クラス__init__ への明示的な呼び出しを含む派生クラスを定義する必要があります。__init__


Anonの投稿で私が驚いたのは、彼が相続理論に反していることを表現しているだけでなく、5人の男が髪の毛を回さずに賛成していたこと、そして2年間で誰も反応しなかったことです。興味深い主題を比較的頻繁に読む必要があるスレッド。


1
私はこの投稿が賛成されるだろうと確信していました。その理由についてはあまり説明しきれないと思います。理解できないように見えるテキストを分析するよりも、反対投票する方が簡単です。Anonの投稿を理解するために長い時間を費やしていましたが、最終的に、それが真剣に記述されており、それほど権威がありませんでした。多分それは継承について知っている誰かにとってほぼ正しいと解釈することができます。しかし、相続についての不安定な概念を持っている誰かによって読まれたとき、私はそれを混乱させます、主題は一般的に
岩水

2
「この投稿が支持されると確信していた...」主な問題は、あなたがパーティーに少し遅れたことであり、ほとんどの人は最初のいくつかの答えを超えて読むことができないかもしれません。ちなみにすばらしい説明+1
Gerrat

これは素晴らしい答えです。
Trilarion 2014年

3
アーロンから引用した文章の「追加」の重要な単語を見逃しました。アーロンの発言は完全に正しいものであり、あなたが言っていることと一致します。
GreenAsJade 14年

1
これは、pythonの設計選択が意味をなした最初の説明です。
Joseph Garvin、2015年

20

編集:(コード変更後)
__init__(または他の関数)を呼び出す必要があるかどうかを通知する方法はありません。継承は明らかにそのような呼び出しなしで機能します。それはすべてコードのロジックに依存します。たとえば、すべて__init__が親クラスで行われている場合は、子クラスを__init__完全にスキップできます。

次の例を検討してください。

>>> class A:
    def __init__(self, val):
        self.a = val


>>> class B(A):
    pass

>>> class C(A):
    def __init__(self, val):
        A.__init__(self, val)
        self.a += val


>>> A(4).a
4
>>> B(5).a
5
>>> C(6).a
12

私の例からスーパーコールを削除しました。親クラスのinitの実装を呼び出す必要があるかどうかを知りたいのですが。
GeorgSchölly09年

次に、タイトルを編集することができます。しかし、私の答えはまだ残っています。
SilentGhost 2009

5

厳格な規則はありません。クラスのドキュメントには、サブクラスがスーパークラスメソッドを呼び出す必要があるかどうかを示す必要があります。スーパークラスの動作を完全に置き換えたい場合もあれば、スーパークラスの動作を強化したい場合もあります。つまり、スーパークラスの呼び出しの前後に独自のコードを呼び出します。

更新:すべてのメソッド呼び出しに同じ基本ロジックが適用されます。コンストラクターは(動作を決定する状態を設定することが多いため)特別な考慮が必要な場合があります。しかし、同じことが、例えば、render()ウィジェットのメソッドに当てはまるかもしれません。

更なる更新: OPPとは?OOPのことですか?いいえ-サブクラスはスーパークラスの設計について何かを知る必要があることがよくあります。内部実装の詳細ではありませんが、スーパークラスがクライアントと(クラスを使用して)持つ基本契約です。これはOOPの原則に決して違反しません。これprotectedが、一般的にOOPで有効な概念です(もちろん、Pythonではそうではありません)。


スーパークラスの呼び出しの前に独自のコードを呼び出したい場合があるとあなたは言いました。これを行うには、OPPに違反する親クラスの実装についての知識が必要です。
GeorgSchölly2009

4

IMO、あなたはそれを呼ぶべきです。スーパークラスがobjectである場合は、そうするべきではありませんが、他の場合では、呼び出さないのは例外的だと思います。他の人がすでに回答したように、__init__たとえば初期化する(追加の)内部状態がない場合など、クラスがそれ自体をオーバーライドする必要がない場合は非常に便利です。


2

はい、__init__コーディングの習慣として、常に基本クラスを明示的に呼び出す必要があります。これを忘れると、微妙な問題や実行時エラーが発生する可能性があります。これは本当です__init__パラメータを取らなくます。これは、コンパイラが暗黙的に基本クラスコンストラクターを呼び出す他の言語とは異なります。Pythonはそれをしません!

常に基本クラスを呼び出す主な理由 _init__は、基本クラスが通常メンバー変数を作成し、それらをデフォルトに初期化する可能性がある。したがって、基本クラスのinitを呼び出さないと、そのコードは実行されず、メンバー変数を持たない基本クラスになります。

class Base:
  def __init__(self):
    print('base init')

class Derived1(Base):
  def __init__(self):
    print('derived1 init')

class Derived2(Base):
  def __init__(self):
    super(Derived2, self).__init__()
    print('derived2 init')

print('Creating Derived1...')
d1 = Derived1()
print('Creating Derived2...')
d2 = Derived2()

これは印刷されます。

Creating Derived1...
derived1 init
Creating Derived2...
base init
derived2 init

このコードを実行します

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.