2014-01-07 50 views
2

如果我有這樣一個類:捕獲許多參數的個數一個在Python __init__

class foo(object): 
    def __init__(self, a, b=None, c=None, d=None): 
    print a, b, c, d 

和派生類是這樣的:

class bar(foo): 
    def __init__(self, *args, **kwargs): 
     if "c" in kwargs: 
      kwargs['c'] = 'else' # CHANGE C IFF IT IS PRESENT 
     super(bar, self).__init__(*args, **kwargs) 

當有人呼叫此構造,他們可以做它是這樣的:

bar('a','b','c','d') 

或者也可以這樣調用它:

bar('a', c='something') 

在第二種情況下,我的構造函數按照計劃工作,但在第一次調用c的情況下,通過參數args數組。這看起來像我不得不觀看args數組的長度以及kwargs,並且這看起來很脆弱以至於無法使用。除了列舉foo中的參數,還有什麼可以改善這種情況嗎? (本身有些脆弱的做法,但更容易識別)。

回答

1

這是更脆。您是否考慮過有人通過foo.__init__的參數列表中不存在的關鍵字參數(例如bar('a', f='something'))?我的建議是需要bar只能採取關鍵字參數,然後使用inspect.getargspec [或相關功能的Python的新版本,尤其是在開始SignatureParameter對象濾波器foo.__init__的參數列表中不存在的鍵(您可以通過內省確定在3.3中]如果參數可能會改變)。當然,由於使用bar的程序員需要知道foo的構造函數的相關參數名稱,但這取決於使用什麼bar,因爲它們可能需要知道foo需要什麼參數無論如何,當有人知道他們通常知道什麼是參數的名稱。

現在我再次看到inspect,我意識到還有另一種方法可以使用:inspect.getcallargs,這可能對您更有用。有了這個,你可以做,例如,inspect.getcallargs(super(bar, self).__init__, *[self, 1], **{'c':3, 'd':4})並獲得以下字典:{'a': 1, 'self': <bar object>, 'b': None, 'c': 3, 'd': 4}。然後您可以修改該字典並將其作爲super(bar, self).__init__(**fixed_dict)或其他類似的東西提供。儘管(getcallargs在傳遞無效參數時會產生相同的錯誤foo.__init__),但您仍然會遇到關鍵字參數不存在於foo.__init__的參數列表中的問題。

2

如何填充kwargsargs

class bar(foo): 
    def __init__(self, *args, **kwargs): 
     for name, value in zip(['a', 'b', 'c', 'd'], args): # <--- 
      kwargs[name] = value       # <--- 
     args =()           # <--- 
     if "c" in kwargs: 
      kwargs['c'] = 'else' 
     super(bar, self).__init__(*args, **kwargs) 

UPDATE

使用inspect.getcallargs替代:比你想象

import inspect 

class bar(foo): 
    def __init__(self, *args, **kwargs): 
     kwargs = inspect.getcallargs(foo.__init__, self, *args, **kwargs) 
     kwargs.pop('self') 
     if 'c' in kwargs: 
      kwargs['c'] = 'else' 
     super(bar, self).__init__(**kwargs) 
+0

這個問題的一部分是我仍然需要知道參數的名稱和順序,如果該基類不在您的控制之下,它可能會改變。我可以很容易地枚舉'__init__'中的命名參數以匹配那些基類,然後我不需要zip,但是如果這個順序改變了,(這就是我所說的脆弱性)。 – boatcoder

+0

@ Mark0978,您可以使用'inspect.getargspec(foo .__ init __)。args [1:]'來代替'['a','b','c','d']'。 – falsetru

+0

@ Mark0978,正如JAB回答的那樣,您也可以使用'inspect.getcallargs'。見http://ideone.com/EjD1r8 – falsetru