スーパーを使用する正しい方法(引数の受け渡し)


99

それで私はPythonの「超熟考」をフォローしていて、彼の例をテストしてみました。

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

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

~ $ 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自体がドキュメントで言及されているベストプラクティスの1つに違反しているようです。つまり、を使用するメソッドsuper*argsおよびを受け入れる必要があり**kwargsます。

さて、ナイト氏は明らかに彼の例が機能することを期待していたので、これは最近のバージョンのPythonで変更されたものですか?私は2.6と2.7をチェックしましたが、両方で失敗しました。

それで、この問題に対処する正しい方法は何ですか?


2
私の優先する方法は、フラットでシンプルな継承階層です。
ミリムース2012年

19
また、お読みください「Pythonのスーパー()と見なさスーパー」バランスの取れた視点を得るために:)
ビョルンPollex

@BjörnPollex:ありがとう!あなたのリンクは、質問への答えを提供します。あなたは、「ルートクラス」から継承を書くことができobject、それがコールに確認しますobject「S __init__正しく。
cha0site 2012年

4
onは、Python 2.5のパラメータ__init__object暗黙のうちに無視することに注意してください。これはPython 2.6で変更されました。
Wilfred Hughes 14

1
@Wilfred:ねえ、実際の質問に答えてくれてありがとう!今、私はエッセイが時代遅れである理由を知っています!
cha0site 2014年

回答:


103

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

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)

収量

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

これが機能するにBaseは、MROの最後から2番目のクラスである必要があります。


4
Base電話してはいけませんsuper(Base, self).__init__()か?
cha0site 2012年

4
これが機能Baseするためには、MROの最後に来る必要があります(を除くobject)。呼び出しobject.__init__は何もしないので、呼び出さなくても大丈夫super(Base, self).__init__()です。実際には、線のsuper(Base, self).__init__()終点であるポイントをホームにドライブすることを含めないほうが明確であると思うBase
unutbu 2012年

25

多くの継承を行う場合(ここではそうです)**kwargs、すべてのパラメーターをを使用して渡すことをお勧めします。次に、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)

6

Pythonのsuper()がsuperと見なされることで説明されているように、1つの方法は、クラスに必要な引数を食べさせ、残りを渡すことです。したがって、呼び出しチェーンがに到達するとobject、すべての引数が食べられ、object.__init__引数なしで(期待どおり)呼び出されます。したがって、コードは次のようになります。

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)

それで、どうE(arg = 10)ですか?この方法は好きではありません。MROを使用するには、事前にMROを知る必要があります。から継承する「ルートクラス」を作成するもう1つの方法は、objectはるかにクリーンなようです。
cha0site 2012年

このメソッドは、呼び出された関数が引数名を共有しない場合に役立ちます。私はに関する実務経験があまりないことを認めていますsuper。そのため、このアプローチは確かにかなり理論的かもしれません。
ビョルンPollex
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.