2017-01-12 25 views
1

arg2のデフォルト値を持つParentクラスがあります。同じ属性に対して異なるデフォルト値を持つサブクラスChildを作成したいとします。 *args**kwargsChildに使用する必要があります。サブクラスのデフォルトのコンストラクタ引数値(親クラスから継承)

私は次のことを試してみましたが、それが動作していません。

class Parent(object): 
    def __init__(self, arg1='something', arg2='old default value'): 
     self.arg1 = arg1 
     self.arg2 = arg2 

     print('arg1:', self.arg1) 
     print('arg2:', self.arg2) 

class Child(Parent): 
    def __init__(self, *args, **kwargs): 
     super(Child, self).__init__(*args, **kwargs) 
     self.arg2 = kwargs.pop('arg2', 'new value') 

これが機能していません。実際に、私は得る:

>>> c = Child() 
arg1: something 
arg2: default value # This is still the old value 
>>> c.arg2 
'new value' # Seems more or less ok 

>>> c = Child('one', 'two') 
arg1: one 
arg2: two 
>>> c.arg2 
'new value' # This is wrong, it has overridden the specified argument 'two' 
+0

これは起こります。 2番目の例では 'arg2'は' kwargs ['arg2'] 'ではなく' args [1] 'です。 – jonrsharpe

+0

@jonrsharpeはい、私はそれを修正する良い方法を見つけることができないようです –

答えて

2

あなたはsuper()に渡す前に、kwargsにデフォルト値を設定する必要があります。

class Child(Parent): 
    def __init__(self, *args, **kwargs): 
     if len(args) < 2 and 'arg2' not in kwargs: 
      kwargs['arg2'] = 'new value' 
     super(Child, self).__init__(*args, **kwargs) 

これは、しかし、そこに埋めるためにどのように多くの引数を知ることに依存している:あなたは同じ値があまりにもargsになっていないことを確認する必要がありますので、これはトリッキーです。

from inspect import getargspec 

class Child(Parent): 
    def __init__(self, *args, **kwargs): 
     super_init = super().__init__ 
     argspec = getargspec(super_init) 
     arg2_index = argspec.args.index('arg2') - 1 # account for self 
     if len(args) < arg2_index and 'arg2' not in kwargs: 
      kwargs['arg2'] = 'new value' 
     super(Child, self).__init__(*args, **kwargs) 

あなたがオフにはるかに良いと思います代わりにすべてのデフォルトを指定:

class Child(Parent): 
    def __init__(self, arg1='something', arg2='new value'): 
     super(Child, self).__init__(arg1=arg1, arg2=arg2) 
+1

このリスクは、arg2の2つの値を持ち、位置的に*と*をキーワードで渡してしまう危険性はありませんか? – jonrsharpe

+0

これは実際に 'Child( 'one'、 'two')'のために失敗します: 'TypeError:__init __()は引数 'arg2'に対して複数の値を持っています –

+1

@jonrsharpe:はい、そのリスクがあります。 –

2

あなたが実際にきたあなたは、これは一般的なケースで作業するためsuper().__init__のイントロスペクションを使用する必要があると思いますクラスの署名を変更しました。基本的に、と:

foo(2, 3) 
foo(a=2, b=3) 

付::

def bar(**kwargs): 
    ... 

あなたが任意のより多くの位置引数で呼び出すことはできません

def foo(a=1, b=2): 
    ... 

あなたは、キーワードによって位置、またはで呼び出すことができます。

bar(2, 3) # TypeError! 

*argsがあなたのすべてのポジション引数を食べるので、あなたの実際のコードは複雑です。


私はあなたを与えることができる最も強力なアドバイスは、あなたがメソッドをオーバーライドするとき、署名を保存することです:

class Child(Parent): 
    def __init__(self, arg1='something', arg2='new value'): 
     super(Child, self).__init__(arg1=arg1, arg2=arg2) 

この(残念ながら)DRYではありません(自分を繰り返してはいけない)などあなたはおそらく好きでしょう - 'something'を2回指定する必要があります。グローバル定数に変換したり、Parent.__init__のシグネチャを変更することができます。

また、適切な引数を正しい方法で渡していることを確認するために、親クラスのシグネチャを使用するためのイントロスペクションを行うこともできますが、それはそれが価値があることを非常に疑うでしょう。

関連する問題