スーパークラスの__init__メソッドが自動的に呼び出されないのはなぜですか?


155

他の一部の言語のように、Python設計者がサブクラスの__init__()メソッドが__init__()スーパークラスのメソッドを自動的に呼び出さないと決定したのはなぜですか?Pythonicと推奨されるイディオムは本当に次のようなものですか?

class Superclass(object):
    def __init__(self):
        print 'Do something'

class Subclass(Superclass):
    def __init__(self):
        super(Subclass, self).__init__()
        print 'Do something else'

2
__init__メソッドを継承するデコレーターを作成し、サブクラスを自動的に検索してデコレートすることもできます。
osa

2
@osa、本当にいいアイデアですね。デコレーターの部分についてもう少し詳しく説明しますか?
Diansheng

1
@osaはい、もっと詳しく!
チャーリーパーカー

回答:


163

Python __init__と他の言語のコンストラクターとの重要な違い、コンストラクターで__init__ないことです。コンストラクター初期化子です(実際のコンストラクター(もしあれば、後で参照してください__new__。ので、すべてのスーパークラスを構築する間(そして、間違いなく、「下に」構築する前にそうすること)は構築していると言うことの一部です。、サブクラスのインスタンスをいる、それは明らかに初期化の場合ではありません、スーパークラスの初期化をスキップ、変更、制御する必要がある多くのユースケースがあるため、サブクラスの初期化の「途中」などで発生します。

基本的には、初期化子のスーパークラスの代表団は、このような代表団はのためにも自動ではありません正確に同じ理由のためにPythonで自動ではありません任意の他の方法-およびそれらの「他の言語」は、任意の自動スーパークラスの委任を行わないことに注意してください他の方法のいずれか... ちょうどである私が述べたように、コンストラクタ(および該当する場合、デストラクタ)、のためではない Pythonのは何__init__です。(の動作__new__も非常に奇妙ですが、実際には質問に直接関連し__new__ていませんが、実際には何かを構築する必要はないので、既存のインスタンスまたはインスタンス以外のインスタンスを返すこともできます...明らかにPythonはあなたにたくさん提供しますあなたが頭に浮かぶ「他の言語」よりもメカニズムをより細かく制御できます。これは、自動委任__new__自体含まれません!-)。


7
私にとって、真の利点は、サブクラスの初期化の任意の時点で(またはまったくではなくスーパークラスの_ init()_を呼び出すことができるということです。
キンドール

53
「-1 __init__はコンストラクタではありません...実際のコンストラクタ...は__new__」です。ご存じのとおり、__new__は他の言語のコンストラクタのようには動作しません。__init__実際には非常によく似ており(新しいオブジェクトの作成中、そのオブジェクトが割り当てられた、新しいオブジェクトにメンバー変数を設定するために呼び出されます)、ほとんどの場合、他の言語で機能を実装する場所ですコンストラクタ。だからそれをコンストラクタと呼んでください!
ベン

8
また、これはかなりばかげた発言だと思います。「すべてのスーパークラスの構築 ...は、明らかに、サブクラスのインスタンスを構築していると言うことの一部です。これは明らかに初期化の場合ではありません」。構築/初期化という言葉には、これを誰にも「自明」にするものはありません。また、__new__スーパークラス__new__も自動的に呼び出されません。したがって、重要な違いは、構築には必ずスーパークラスの構築が含まれるという主張ですが、初期化__new__は、コンストラクターである主張と矛盾しません。
ベン

36
以下のためのpythonのドキュメントから、実際には__init__docs.python.org/reference/datamodel.html#basic-customization:「上の特殊な制約としてコンストラクタ、何も値が返されないことがあり、そうすることはTypeErrorが実行時に上げることになります"(私の鉱山を強調します)。つまり、公式__init__のコンストラクタです。
ベン

3
Python / Java用語で__init__は、はコンストラクタと呼ばれます。このコンストラクターは、オブジェクトが完全に構築され、最終的なランタイムタイプを含むデフォルトの状態に初期化された後に呼び出される初期化関数です。これは、静的に型指定され、割り当てられたオブジェクトが未定義の状態で呼び出されるC ++コンストラクターと同等ではありません。これらもとは異なる__new__ため、実際には少なくとも4種類の割り当て/構築/初期化関数があります。言語は混合した用語を使用しており、重要な部分は動作であり、用語ではありません。
Elazar 2017年

36

人々が「Zen of Python」をオウムするとき、それが何かの正当化であるかのように、私は多少恥ずかしいです。それは設計哲学です。特定の設計上の決定は、常により具体的な用語で説明できますが、そうでなければなりません。そうでなければ、「Zen of Python」が何かをするための言い訳になります。

その理由は単純です。基本クラスを構築する方法とまったく同様の方法で、必ずしも派生クラスを構築する必要はありません。あなたはより多くのパラメータを持っているかもしれません、より少なく、それらは異なる順序にある​​か、まったく関係がないかもしれません。

class myFile(object):
    def __init__(self, filename, mode):
        self.f = open(filename, mode)
class readFile(myFile):
    def __init__(self, filename):
        super(readFile, self).__init__(filename, "r")
class tempFile(myFile):
    def __init__(self, mode):
        super(tempFile, self).__init__("/tmp/file", mode)
class wordsFile(myFile):
    def __init__(self, language):
        super(wordsFile, self).__init__("/usr/share/dict/%s" % language, "r")

これは、だけでなく、すべての派生メソッドに適用されます__init__


6
この例は特別なものを提供していますか?静的言語でもこれを行うことができます
jean 2015年

18

JavaおよびC ++では、メモリレイアウトのため、基本クラスコンストラクターを呼び出す必要があります。

BaseClassメンバーを持つクラスがあり、メンバーを追加するfield1新しいクラスSubClassを作成field2する場合、のインスタンスにSubClassfield1およびのスペースが含まれますfield2。すべての継承クラスが独自のコンストラクタでの初期化を繰り返す必要がない限り、BaseClassを埋めるためのコンストラクタがfield1必要ですBaseClass。場合やfield1プライベートで、その後、クラスを継承することはできません初期化しますfield1

PythonはJavaまたはC ++ではありません。すべてのユーザー定義クラスのすべてのインスタンスは、同じ「形状」を持っています。基本的には、属性を挿入できる単なる辞書です。初期化が行われる前は、すべてのユーザー定義クラスのすべてのインスタンスはほとんど同じですです。これらは、まだ保存されていない属性を保存する場所にすぎません。

そのため、Pythonサブクラスがその基本クラスコンストラクターを呼び出さないことは、完全に理にかなっています。必要に応じて、属性自体を追加することもできます。階層内の各クラスの指定された数のフィールド用に予約されているスペースはなくBaseClassSubClassメソッドからのコードによって追加された属性とメソッドからのコードによって追加された属性に違いはありません。

よくあることですが、独自のカスタマイズを行う前にのSubClassすべてBaseClassの不変条件を実際に設定したい場合は、はい、呼び出すだけでかまいませんBaseClass.__init__()(またはを使用しますsuperが、複雑で、独自の問題が発生する場合があります)。しかし、そうする必要はありません。そして、あなたはそれを前、後、または異なる引数で行うことができます。地獄、あなたが望むならあなたはBaseClass.__init__完全に別のメソッドから呼び出すことができます__init__; 多分あなたはいくつかの奇妙な怠惰な初期化のことを行っています。

Pythonは物事をシンプルに保つことでこの柔軟性を実現しています。オブジェクトを初期化するには、に__init__属性を設定するメソッドを記述しselfます。それでおしまい。これはメソッドなので、メソッドとまったく同じように動作します。最初に行わなければならないこと、または他のことを行わない場合に自動的に行われることについて、他に奇妙で直感的でないルールはありません。それが果たす必要がある唯一の目的は、初期属性値を設定するためのオブジェクト初期化中に実行するフックであることであり、それはまさにそれを行います。他のことを実行したい場合は、コードに明示的に記述します。


2
C ++については、「メモリレイアウト」とは関係ありません。Pythonが提供するものと同じ初期化モデルがC ++で実装されている可能性があります。構築/破壊がC ++での方法と同じである唯一の理由は、リソース管理(RAII)に信頼性のある適切に動作する機能を提供するという設計上の決定によるものであり、自動的に生成することもできます(つまり、コードや人的エラーが少ないことを意味します)。それらのルール(それらの呼び出し順序)は厳密に定義されているため、コンパイラによって。確かではありませんが、Javaはおそらくこのアプローチに従って、さらに別のPOLA Cのような言語になりました。
Alexander Shukaev

10

「明示的は暗黙的よりも優れています。」「自己」を明示的に記述する必要があることを示すのと同じ理由です。

結局、それは利点だと思います-スーパークラスのコンストラクターの呼び出しに関してJavaが持っているすべてのルールを説明できますか?


8
大部分はあなたに同意しますが、Javaのルールは実際にはかなり単純です。別のコンストラクタを特に要求しない限り、引数なしのコンストラクターが呼び出されます。
ローレンスゴンサルベス

1
@Laurence-親クラスが引数なしのコンストラクターを定義しない場合はどうなりますか?引数なしのコンストラクターが保護またはプライベートの場合はどうなりますか?
Mike Axiak

8
明示的に呼び出そうとした場合とまったく同じです。
ローレンスゴンサルベス

8

多くの場合、サブクラスにはスーパークラスに渡すことができない追加のパラメーターがあります。


7

現在、多重継承の場合のメソッド解決順序を説明するかなり長いページがあります:http : //www.python.org/download/releases/2.3/mro/

コンストラクターが自動的に呼び出された場合、少なくとも同じ長さの別のページが発生する順序を説明する必要があります。それは地獄でしょう...


これが正解でした。Pythonは、サブクラスとスーパークラスの間で渡される引数のセマンティクスを定義する必要があります。残念ながら、この回答は賛成されていません。多分それがいくつかの例で問題を示したなら?
ゲイリーワイス

5

混乱を避けるために__init__()、child_classに__init__()クラスがない場合にbase_class メソッドを呼び出すことができることを知っておくと便利です。

例:

class parent:
  def __init__(self, a=1, b=0):
    self.a = a
    self.b = b

class child(parent):
  def me(self):
    pass

p = child(5, 4)
q = child(7)
z= child()

print p.a # prints 5
print q.b # prints 0
print z.a # prints 1

実際、PythonのMRO __init__()は、子クラスで見つからない場合、親クラスで検索します。__init__()子クラスにすでにメソッドがある場合は、親クラスのコンストラクターを直接呼び出す必要があります。

たとえば、次のコードはエラーを返します:クラスの親:def init(self、a = 1、b = 0):self.a = a self.b = b

    class child(parent):
      def __init__(self):
        pass
      def me(self):
        pass

    p = child(5, 4) # Error: constructor gets one argument 3 is provided.
    q = child(7)  # Error: constructor gets one argument 2 is provided.

    z= child()
    print z.a # Error: No attribute named as a can be found.

3

たぶん__init__、サブクラスがオーバーライドする必要があるメソッドです。サブクラスでは、クラス固有のコードを追加する前に親の関数を実行する必要がある場合や、親の関数を呼び出す前にインスタンス変数を設定する必要がある場合があります。Pythonがこれらの関数を呼び出すのが最も適切な時期を知ることができる方法はないので、推測すべきではありません。

それらがあなたを揺さぶらないならば、それ__init__はちょうどもう一つの機能であると考えてください。問題の関数がdostuff代わりにあったとしても、Pythonが親クラスの対応する関数を自動的に呼び出すようにしますか?


2

ここで非常に重要な考慮事項の1つは、への自動呼び出しではsuper.__init__()、その初期化メソッドがいつ呼び出され、どのような引数を使用するかを、仕様上禁止していることです。自動的にそれを呼び出すことを避け、プログラマに明示的にその呼び出しを行わせることは、多くの柔軟性を伴います。

結局のところ、クラスBがクラスAから派生しているからA.__init__()といって、と同じ引数で呼び出すことができる、または呼び出すべきであるという意味ではありませんB.__init__()。呼び出しを明示的にすることは、プログラマーがB.__init__()完全に異なるパラメーターで定義し、そのデータを使用して計算を行い、A.__init__()そのメソッドに適切な引数で呼び出し、その後いくつかの後処理を行うことを意味します。この種の柔軟性は、実行前または実行直後のいずれかA.__init__()からB.__init__()暗黙的に呼び出された場合に達成するのが困難ですB.__init__()

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