2017-07-31 19 views
7

コードがあります。defaultdictを継承し、サブクラスメソッドでそのコピーメソッドを使用するにはどうすればよいですか?

from collections import defaultdict 
class A(defaultdict): 
    def __init__(self): 
    super(A, self).__init__(lambda :0) 
    self.x = 1 

    def my_copy(self): 
    return self.copy() 

if __name__ == '__main__': 
    a = defaultdict(lambda :0) 
    b = a.copy() # no error when using the base class directly 
    a = A() 
    b = a.my_copy() 

エラーがあります:

Traceback (most recent call last): 
    File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1591, in <module> 
    globals = debugger.run(setup['file'], None, None, is_module) 
    File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1018, in run 
    pydev_imports.execfile(file, globals, locals) # execute the script 
    File "/Applications/PyCharm.app/Contents/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile 
    exec(compile(contents+"\n", file, 'exec'), glob, loc) 
    File "/Users/liu/project/scir/pytorch_test/t.py", line 14, in <module> 
    b = a.my_copy() 
    File "/Users/liu/project/scir/pytorch_test/t.py", line 8, in my_copy 
    return self.copy() 
TypeError: __init__() takes 1 positional argument but 3 were given 

私はコピー方法を継承する方法がわからないと、私は3引数を与える理由も分かりません。

答えて

7

copyを呼び出すと、default_factory関数とデータを渡すために、defaultdictが引数を持つコンストラクタを呼び出します。

あなたのコンストラクタは引数をとらないので、固定された工場で空のdictsを構築することしかできません。

は、このようなあなたのコンストラクタを修正:

def __init__(self,*args): 

しかし、あなたは(あなたが望んでいないこと)空になり、母親のクラスまたはコピーした辞書にargsを渡す必要があります。

デフォルトの工場を専門にしているので、あなたはargsが空の場合は特殊なケースを作る必要があります:三元と

class A(defaultdict): 
    def __init__(self,*args): 
    if args: 
     super(A, self).__init__(*args) 
    else: 
     super(A, self).__init__(int) # better than lambda : 0 

それとも単純:

class A(defaultdict): 
    def __init__(self,*args): 
    super(A, self).__init__(*(args or (int,))) 
  • argsが空でない場合(copyから呼び出された場合)、コピーは元のプロパティ(関数&データ)を取ります。
  • argsが空の場合は、新しいdictを作成しているため、デフォルトのファクトリ引数が修正されます。

脇:(lambda :0)(int)に置き換えることができます。

EDIT:(最初の引数がintない場合は警告が多分)ユーザーは、最初の引数と力intを無視するだろうデフォルトを変更することはできませんことを確認し、より複雑な方法が、:

super(A, self).__init__(*([int]+list(args[1:]))) 

ことしかし、私は多くの議論を無視するという考えが好きではありません。

結論として、組み込み型の継承は一般的ではありません。慎重に使用する必要があります(pandasデータフレームでこれを行う別の例を参照してください:building a class from an existing one)。場合によっては、引数としてdefaultdictを持つクラスを作成し、使用する予定のメソッドのみを模倣/中継すると、副作用が少なくなります。

+0

これにより、OPが望んでいない可能性のある別の工場をユーザーが指定できるようになります。 –

+1

@brunodesthuilliersが私の編集を参照してください。しかし、私の結論は、組込み型から安全に継承するのは難しいことです。 –

2

defaultdict.__init__()は、3つの引数:self(もちろん), an optional factory callable for missing keys and an optional set of key:values (which cand be either a dict or a sequence of(キー、値)のペアをとります。

defaultdict.copy()インスタンスは新しいdefaultdictインスタンスを作成し、それをfactory呼び出し可能にし、その現在のキー:値セットのシャローコピーを渡します。

サブクラスの__init__は、引数としてselfしか受け取りませんが、3つで呼び出されます。ここ

修正がそう、それは両方のケースを扱うことができるA.__init__を書き換えることである:

class A(defaultdict): 
    def __init__(self, *args): 
     # make sure we force the factory 
     args = (int,) + args[1:] 
     super(A, self).__init__(*args) 
1

私は答えに小さなコメントしたものを拡大することを決めました。既に与えられた回答で完璧な分析が行われたが、私は提案された議論の変更を嫌う。 defaultdictとその下にあるdictの両方は、些細な署名(引数の使用)を持っています。以下のコードは、引数には触れず、元の実装に変更されていません。

また、kwargsは保持されます。 A(a=1,b=2)が動作します。

関連する問題