2017-07-31 101 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時,defaultdict調用帶參數的構造函數來傳遞default_factory函數和數據。

你的構造函數不帶任何參數,所以它只能用固定工廠建立空的字典。

修復你的構造是這樣的:

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爲空時,表示您正在創建一個新的字典,所以它只是修復了默認的工廠參數。

除此之外:您可以用(int)替代(lambda :0)

編輯:更復雜的方式,但它可以確保用戶無法更改默認會忽略第一個參數和力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__()需要三個參數: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(key,value)`pairs)。

defaultdict.copy()將創建一個新的defaultdict實例並將其傳遞給它的factory可調用並且它是當前鍵的淺表副本:值集。

您的子類的__init__僅需要self作爲參數,但最終會被三個參數調用。

這裏的解決辦法是重寫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和底層字典都有一個非平凡的簽名(使用參數)。下面的代碼不接觸參數,並將它們傳遞不變原來的實現:

def __init__(self, *args, **kwargs): 
    super(A, self).__init__(*args, **kwargs) 
    self.default_factory = int 

另外,kwargs被保留,例如A(a=1,b=2)工程。