2012-01-23 46 views
52

私はPython's Super Considered Harmfulをフォローし、彼の例を試しに行った。super(引数の受け渡し)を使う正しい方法

__init__異なる引数が必要なメソッドを処理するときにsuperを呼び出す正しい方法を示すはずのExample 1-3は、フラットアウトが機能しません。

これは私が得るものです:

~ $ python example1-3.py 
MRO: ['E', 'C', 'A', 'D', 'B', 'object'] 
E arg= 10 
C arg= 10 
A 
D arg= 10 
B 
Traceback (most recent call last): 
    File "Download/example1-3.py", line 27, in <module> 
    E(arg=10) 
    File "Download/example1-3.py", line 24, in __init__ 
    super(E, self).__init__(arg, *args, **kwargs) 
    File "Download/example1-3.py", line 14, in __init__ 
    super(C, self).__init__(arg, *args, **kwargs) 
    File "Download/example1-3.py", line 4, in __init__ 
    super(A, self).__init__(*args, **kwargs) 
    File "Download/example1-3.py", line 19, in __init__ 
    super(D, self).__init__(arg, *args, **kwargs) 
    File "Download/example1-3.py", line 9, in __init__ 
    super(B, self).__init__(*args, **kwargs) 
TypeError: object.__init__() takes no parameters 

object自体がsuperを使用する方法が*args**kwargsを受け入れなければならないということである文書で言及したベストプラクティスの1に違反しているようです。

明らかにKnight氏は彼の例題がうまくいくと予想していたので、最近のバージョンのPythonではこれが変更されていますか?私は2.6と2.7をチェックし、両方で失敗します。

この問題を解決する正しい方法は何ですか?

+2

私の好みの方法は次のとおりです。フラットおよび単純継承階層。 – millimoose

+14

また、バランスの取れたビューを得るために、["Pythonのsuper()と思っているスーパー"](http://rhettinger.wordpress.com/2011/05/26/super-considered-super/)も読んでください:) –

+0

@BjörnPollex:ありがとう!あなたのリンクは質問に対する答えを提供します: 'object'から継承した' 'root class ''を書くことができ、' object'''を正しく呼び出すことができます。 – cha0site

答えて

55

2つのクラスには、いくつかのパラメータ名が共通していることがあります。この場合、キーと値のペアを**kwargsから外したり、*argsから削除することはできません。代わりに、あなたはobjectとは異なり、/吸収Baseクラスを定義することができ、引数を無視します:

class Base(object): 
    def __init__(self, *args, **kwargs): pass 

class A(Base): 
    def __init__(self, *args, **kwargs): 
     print "A" 
     super(A, self).__init__(*args, **kwargs) 

class B(Base): 
    def __init__(self, *args, **kwargs): 
     print "B" 
     super(B, self).__init__(*args, **kwargs) 

class C(A): 
    def __init__(self, arg, *args, **kwargs): 
     print "C","arg=",arg 
     super(C, self).__init__(arg, *args, **kwargs) 

class D(B): 
    def __init__(self, arg, *args, **kwargs): 
     print "D", "arg=",arg 
     super(D, self).__init__(arg, *args, **kwargs) 

class E(C,D): 
    def __init__(self, arg, *args, **kwargs): 
     print "E", "arg=",arg 
     super(E, self).__init__(arg, *args, **kwargs) 

print "MRO:", [x.__name__ for x in E.__mro__] 
E(10) 

はこれが機能するために、BaseはMROで最後から二番目のクラスでなければならないこと

MRO: ['E', 'C', 'A', 'D', 'B', 'Base', 'object'] 
E arg= 10 
C arg= 10 
A 
D arg= 10 
B 

注意を与えます。

+2

'Base(スーパー、ベース).__ init __()'を呼び出すべきではありませんか? – cha0site

+2

これが機能するためには、 'Base'はMROの最後に来なければなりません(' object'を除く)。 'object .__ init__'を呼び出すことは何もしないので、' super(Base、self).__ init __() 'を呼び出さなくてもかまいません。実際には、 'Base(基本)、self(自己).__ init __()'を含めて、 'Base'が行の終わりである点をホームに追いやらないようにするのは明らかです。 – unutbu

5

Python's super() considered superで説明したように、1つの方法は、クラスに必要な引数を食べさせ、残りを渡すことです。したがって、コールチェーンがobjectに達すると、すべての引数が使用され、object.__init__は引数なしで呼び出されます(期待どおり)。あなたが使用した直後にそれらを使用すると、継承の多くを持っているつもりなら(つまり、ここではケースだ)私はあなたが**kwargsを使用して、すべてのパラメータを渡すことをお勧め

class A(object): 
    def __init__(self, *args, **kwargs): 
     print "A" 
     super(A, self).__init__(*args, **kwargs) 

class B(object): 
    def __init__(self, *args, **kwargs): 
     print "B" 
     super(B, self).__init__(*args, **kwargs) 

class C(A): 
    def __init__(self, arg, *args, **kwargs): 
     print "C","arg=",arg 
     super(C, self).__init__(*args, **kwargs) 

class D(B): 
    def __init__(self, arg, *args, **kwargs): 
     print "D", "arg=",arg 
     super(D, self).__init__(*args, **kwargs) 

class E(C,D): 
    def __init__(self, arg, *args, **kwargs): 
     print "E", "arg=",arg 
     super(E, self).__init__(*args, **kwargs) 

print "MRO:", [x.__name__ for x in E.__mro__] 
E(10, 20, 30) 
+0

だから、 'E(arg = 10)'はどうでしょうか?私はこの方法が気に入らないので、事前にMROを知っておく必要があります。私が "オブジェクトから継承する"ルートクラス "を書くもう1つの方法は、はるかにクリーンなようです。 – cha0site

+0

このメソッドは、呼び出された関数が引数名を共有しない場合に便利です。私は「スーパー」との実践的な経験はあまりないと認めているので、このアプローチは確かに理論的かもしれません。 –

14

、その後、pop:だからあなたのコードは次のようになります。 (上層階でそれらを必要としない限り)。

class First(object): 
    def __init__(self, *args, **kwargs): 
     self.first_arg = kwargs.pop('first_arg') 
     super(First, self).__init__(*args, **kwargs) 

class Second(First): 
    def __init__(self, *args, **kwargs): 
     self.second_arg = kwargs.pop('second_arg') 
     super(Second, self).__init__(*args, **kwargs) 

class Third(Second): 
    def __init__(self, *args, **kwargs): 
     self.third_arg = kwargs.pop('third_arg') 
     super(Third, self).__init__(*args, **kwargs) 

これは、この種の問題を解決する最も簡単な方法です。

third = Third(first_arg=1, second_arg=2, third_arg=3) 
関連する問題