web-dev-qa-db-ja.com

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

なぜ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'
143
jrdioko

Pythonの__init__と他の言語との決定的な違いconstructorsは、__init__notコンストラクターであることです。これは初期化子(実際のコンストラクター(もしあれば、しかし、後で見る;-)は__new__であり、再び完全に異なる動作をします)。 構築中すべてのスーパークラス(そして、間違いなく、「前」に構築を続ける)は、明らかに構築中サブクラスのインスタンスであり、それは明らかにinitializingの場合ではありません。スーパークラスの初期化をスキップ、変更、制御する必要がある多くのユースケースがあるため、サブクラスの初期化の「途中」であったとしても、などなど。

基本的に、イニシャライザのスーパークラス委任はPythonで自動ではありません。まったく同じ理由で、そのような委任もany他のメソッドで自動ではないためです。言語」は、他のメソッドの自動スーパークラス委任も行いません...justコンストラクター(および該当する場合は、デストラクター) not Pythonの__init__とは何ですか。 (__new__の振る舞いも非常に独特ですが、実際にはあなたの質問に直接関連していませんが、__new__は実際には必ずしも何かを構築する必要がないような固有のコンストラクタであるため、既存のインスタンス、または-instance ...明らかにPythonはlotを念頭に置いている "他の言語"よりもメカニクスをより多く制御します。これはalso__new__自体に自動委任がないことを含みます!-)。

157
Alex Martelli

「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__だけでなく、すべての派生メソッドに適用されます。

36
Glenn Maynard

JavaおよびC++ 必須メモリレイアウトのために基本クラスコンストラクターが呼び出されること。

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

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

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

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

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

18
Ben

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

最終的には利点だと思います。スーパークラスのコンストラクターの呼び出しに関してJavaが持つすべてのルールを列挙できますか?

10
Mike Axiak

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

8
John La Rooy

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

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

7
viraptor

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

例:

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は、childrenクラスで見つからない場合、親クラスで__init__()を探します。子クラスに既に__init__()メソッドがある場合は、親クラスのコンストラクターを直接呼び出す必要があります。

たとえば、次のコードはエラーを返します。class parent: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.
4
Keyvanrm

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

それらがあなたに動揺しないなら、__init__は単なる別の関数であると考えてください。問題の関数がdostuffであった場合、Pythonが親クラスの対応する関数を自動的に呼び出すようにしますか?

3
Kirk Strauser

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

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

2
flow