なぜ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'
Pythonの__init__
と他の言語との決定的な違いconstructorsは、__init__
がnotコンストラクターであることです。これは初期化子(実際のコンストラクター(もしあれば、しかし、後で見る;-)は__new__
であり、再び完全に異なる動作をします)。 構築中すべてのスーパークラス(そして、間違いなく、「前」に構築を続ける)は、明らかに構築中サブクラスのインスタンスであり、それは明らかにinitializingの場合ではありません。スーパークラスの初期化をスキップ、変更、制御する必要がある多くのユースケースがあるため、サブクラスの初期化の「途中」であったとしても、などなど。
基本的に、イニシャライザのスーパークラス委任はPythonで自動ではありません。まったく同じ理由で、そのような委任もany他のメソッドで自動ではないためです。言語」は、他のメソッドの自動スーパークラス委任も行いません...justコンストラクター(および該当する場合は、デストラクター) not Pythonの__init__
とは何ですか。 (__new__
の振る舞いも非常に独特ですが、実際にはあなたの質問に直接関連していませんが、__new__
は実際には必ずしも何かを構築する必要がないような固有のコンストラクタであるため、既存のインスタンス、または-instance ...明らかにPythonはlotを念頭に置いている "他の言語"よりもメカニクスをより多く制御します。これはalso__new__
自体に自動委任がないことを含みます!-)。
「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__
だけでなく、すべての派生メソッドに適用されます。
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__
メソッドを記述します。それでおしまい。メソッドであるため、メソッドとまったく同じように動作します。最初にやらなければならないことや、他のことをしないと自動的に発生することについて、他の奇妙で直感に反するルールはありません。提供する必要がある唯一の目的は、オブジェクトの初期化中に実行して初期属性値を設定するフックにすることです。他のことをしたい場合は、コードで明示的に記述します。
「明示的は暗黙的よりも優れています。」これは、「自己」を明示的に記述する必要があることを示す理由と同じです。
最終的には利点だと思います。スーパークラスのコンストラクターの呼び出しに関してJavaが持つすべてのルールを列挙できますか?
多くの場合、サブクラスには、スーパークラスに渡すことができない追加のパラメーターがあります。
現在、多重継承の場合のメソッド解決順序を説明するかなり長いページがあります。 http://www.python.org/download/releases/2.3/mro/
コンストラクターが自動的に呼び出された場合、その発生の順序を説明する少なくとも同じ長さの別のページが必要になります。それは地獄だろう...
混乱を避けるために、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.
たぶん__init__
は、サブクラスがオーバーライドする必要があるメソッドです。サブクラスは、クラス固有のコードを追加する前に親の関数を実行する必要がある場合と、親の関数を呼び出す前にインスタンス変数を設定する必要がある場合があります。 Pythonがこれらの関数を呼び出すのがいつ最適かを知る方法はないので、推測すべきではありません。
それらがあなたに動揺しないなら、__init__
は単なる別の関数であると考えてください。問題の関数がdostuff
であった場合、Pythonが親クラスの対応する関数を自動的に呼び出すようにしますか?
ここで非常に重要な考慮事項の1つは、super.__init__()
の自動呼び出しで、設計により、その初期化メソッドがいつ呼び出され、どの引数を使用するかを禁止することです。自動的に呼び出すのを避け、プログラマが明示的にその呼び出しを行うことを要求することは、多くの柔軟性を伴います。
結局のところ、クラスBがクラスAから派生しているからといって、A.__init__()
はB.__init__()
と同じ引数で呼び出すことができる、または呼び出す必要があるという意味ではありません。呼び出しを明示的にすると、プログラマーは例えばB.__init__()
を完全に異なるパラメーターで定義し、そのデータで計算を行い、そのメソッドに適切な引数でA.__init__()
を呼び出してから、後処理を行います。 A.__init__()
が実行される前または直後に、B.__init__()
から暗黙的にB.__init__()
が呼び出される場合、この種の柔軟性は達成しにくいでしょう。