2017-08-25 111 views
1

有沒有辦法阻止一些但不是所有的參數被髮送到超類?是否可以選擇哪些kwargs傳遞給Python中的超類?

我有一個驗證用戶輸入的基類:從Base

class Base(object): 
    def __init__(self, **kwargs): 
     self.kwargs = kwargs 
     super(Base, self).__init__() 

    @staticmethod 
    def check_integrity(allowed, given): 
     """ 
     Verify user input. 
     :param allowed: list. Keys allowed in class 
     :param given: list. Keys given by user 
     :return: 
     """ 
     for key in given: 
      if key not in allowed: 
       raise Exception('{} not in {}'.format(key, allowed)) 

>>> base = Base() 
>>> print base.__dict__ 
output[0]: {'kwargs': {}} 

A繼承和使用方法,以檢查它的關鍵字

class A(Base): 
    def __init__(self, **kwargs): 
     super(A, self).__init__(**kwargs) 
     self.default_properties = {'a': 1, 
            'b': 2} 

     self.check_integrity(self.default_properties.keys(), kwargs.keys()) 

>>> a = A(a=4) 
>>> print a.__dict__ 
output[1]: {'default_properties': {'a': 1, 'b': 2}, 'kwargs': {'a': 4}} 

我還要提到的是我已經拉出我在這個類中用於更新類屬性的另一種方法,因爲它是一個與問題無關的併發症(因此在上例中爲什麼a未更新爲4

在問題出現時,試圖從A繼承和增加額外kwargs子類:

class B(A): 
    def __init__(self, **kwargs): 
     super(B, self).__init__(**kwargs) 

     self.default_properties = {'a': 2, 
            'c': 3, 
            'd': 4} 

     self.check_integrity(self.default_properties.keys(), kwargs.keys()) 



>>> b = B(d=5) 


Traceback (most recent call last): 
    File "/home/b3053674/Documents/PyCoTools/PyCoTools/Tests/scrap_paper.py", line 112, in <module> 
    b = B(d=5) 
    File "/home/b3053674/Documents/PyCoTools/PyCoTools/Tests/scrap_paper.py", line 96, in __init__ 
    super(B, self).__init__(**kwargs) 
    File "/home/b3053674/Documents/PyCoTools/PyCoTools/Tests/scrap_paper.py", line 92, in __init__ 
    self.check_integrity(self.default_properties.keys(), kwargs.keys()) 
    File "/home/b3053674/Documents/PyCoTools/PyCoTools/Tests/scrap_paper.py", line 84, in check_integrity 
    raise Exception('{} not in {}'.format(key, allowed)) 
Exception: d not in ['a', 'b'] 

這裏d被傳遞到超類,即使它只是在子類必須的。但是,ab自變量用於A,應該從B轉換爲A

+1

'del kwargs ['d']'? –

+0

需要通過'kwargs'來超類那些不在'self.default_properties'.Is this your expectation @CiaranWelsh? – Rajez

+0

我想從'B()。default_properties()'''''''''a'和'b'從B()。default_properties()'傳遞給'A',而不是'd'。 – CiaranWelsh

回答

3

有沒有辦法阻止一些但不是所有的參數被髮送到超類?

嗯,是的很簡單:不要通過它們。你應該知道哪些參數你的類需要和它的哪些參數超(ES)花費太多,所以只能通過超預期的內容:

class Base(object): 
    def __init__(self, arg1, arg2): 
     self.arg1 = arg1 
     self.arg2 = arg2 

class Child(object): 
    def __init__(self, arg1, arg2, arg3): 
     super(Child, self).__init__(arg1, arg2) 
     self.arg3 = arg3 

以上是樸素簡單的可讀性和可維護性和保證下,工作。如果你想默認值它不是一個問題或者:

class Base(object): 
    def __init__(self, arg1=1, arg2=2): 
     self.arg1 = arg1 
     self.arg2 = arg2 

class Child(object): 
    def __init__(self, arg1=1, arg2=2, arg3=3): 
     super(Child, self).__init__(arg1, arg2) 
     self.arg3 = arg3 

現在,如果你的類的責任是對給定的「架構」(default_properties在你的代碼段)驗證任意用戶輸入,的確是有幾個邏輯錯誤在你的代碼中 - 主要是你1.在初始化器中驗證你的輸入,2.在覆蓋你的對象的default_properties之前調用父類的初始化器,所以當超類初始化器被調用時它不會根據正確的模式進行驗證。您還正在定義default_properties在你的初始化實例屬性,所以如果你剛換的指示首先定義default_propertie,然後才調用父類的初始化這一個將重新定義default_properties

一個簡單的解決方法是隻讓default_properties一類屬性:

class Base(object): 

    # empty by default 
    default_properties = {} 

    def __init__(self, **kwargs):     
     self.kwargs = kwargs 
     # object.__init__ is a noop so no need for a super call here 
     self.check_integrity()     

    def check_integrity(self): 
     """ 
     Verify user input. 
     """ 
     for key in self.kwargs: 
      if key not in self.default_properties: 
       raise ValueError('{} not allowed in {}'.format(key, self.default_properties)) 

,然後你不必重寫初始化所有:

class A(Base): 
    default_properties = {'a': 1, 
          'b': 2} 


class B(A): 
    default_properties = {'a': 1, 
          'b': 2, 
          'd': 3} 

,你就別e - check_integrity將使用當前實例的類default_properties,並且您不必關心「正在選擇傳遞給超類的哪些kwargs」。

現在這仍然方式簡單化作爲輸入驗證framewoek有效地開展工作,特別是如果你想繼承...如果BA適當的子類,它應該能夠添加到default_properties,而不必重新定義完全(這是一個明顯的幹違規)。用戶輸入驗證通常比僅僅檢查參數名稱涉及更多...您可能想要研究其他庫/框架如何解決問題(Django的表單在這裏可以想到)。

+0

嗨bruno,非常感謝您的詳細解答 - 它對我來說非常寶貴(作爲一名自學Python程序員)。我只是有幾個問題。首先,當我對你的建議進行修改時,在Base類中調用'self.check_integrity()'會產生一個'NameError'。這是一個[link](http://www.tutorialspoint.com/execute_python_online.php?PID=0Bw_CjBb95KQMbElGSW9IYUdGSHM)來演示錯誤。其次,是否有必要定義一個空的'default_properties'?這有什麼價值?謝謝。 – CiaranWelsh

+0

@CiaranWelsh在我的代碼片段中確實存在錯誤,現在已修復。空的'default_properties'不是_strictly_必需的(並且不需要爲空),但是如果沒有'check_integrity()',那麼會爲不帶'default_properties'的子類中斷。它也使意圖更清晰。 –

相關問題