168

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'
4

10 に答える 10

174

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

基本的に、イニシャライザのスーパークラス委譲は Python では自動ではありません。これとまったく同じ理由で、そのような委譲は他のメソッドで自動ではありません。他の方法のいずれか...コンストラクタ(および該当する場合はデストラクタ)のためだけであり、前述のように、 Pythonのものではありません__init__。(の動作__new__も非常に独特ですが、実際にはあなたの質問とは直接関係あり__new__ませんが、実際には必ずしも何かを構築する必要がないような独特のコンストラクターであるため、既存のインスタンス、または非インスタンスを完全に返すことができます...明らかにPythonはあなたに多くを提供しますあなたが考えている「他の言語」よりもメカニズムをより詳細に制御できます。これには、それ自体に自動委譲がないこと__new__含まれます!-)。

于 2010-09-23T22:07:53.707 に答える
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__

于 2010-09-23T22:08:51.020 に答える
20

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

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

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

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

よくあることですが、独自のカスタマイズを行う前にSubClassすべての の不変条件を実際に設定したい場合は、はい、単に呼び出すことができます(または を使用しますが、それは複雑で、場合によっては独自の問題があります)。しかし、そうする必要はありません。また、前、後、または異なる引数で実行できます。地獄、あなたが望むなら、完全に;以外のメソッドから を呼び出すことができます。奇妙な遅延初期化が行われている可能性があります。BaseClassBaseClass.__init__()superBaseClass.__init____init__

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

于 2011-10-24T05:20:01.453 に答える
10

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

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

于 2010-09-23T21:59:08.550 に答える
8

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

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

于 2010-09-23T22:12:23.843 に答える
7

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

于 2010-09-23T22:00:32.753 に答える
3

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

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

于 2010-09-23T22:13:15.167 に答える
2

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

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

于 2010-09-24T00:17:26.303 に答える