2014-10-06 47 views
1

考慮下面的示例代碼:繼承在Python 3中如何與kwargs一起工作?

#!/usr/bin/python3 

class Parent: 
    def __init__(self, **kwargs): 
     # Expect data1, data2 
     self.values = kwargs 

    # Accessor method to set object values 
    def setvalue(self, k, v): 
     self.values[k] = v 

    # Accessor method to get/return object values 
    def getvalue(self, k): 
     return self.vlaues.get(k, None) 

class Child(Parent): 
    def __init__(self, **kwargs): 
     # Expect data3, data4 
     self.values = kwargs 

    # Accessor method to set object values 
    def setvalue(self, k, v): 
     self.values[k] = v 

    # Accessor method to get/return object values 
    def getvalue(self, k): 
     return self.values.get(k, None) 

def main(): 
    new_object = Child(data1 = 'abc', data2 = 'def', 
     data3 = 'ghi', data4 = 'jkl') 

    for v in new_object.values: 
     print('{}: {}'.format(v, new_object.getvalue(v))) 

    new_object.setvalue('data1', 'tuv') 
    new_object.setvalue('data4', 'xyz') 

    for v in new_object.values: 
     print('{}: {}'.format(v, new_object.getvalue(v))) 

if __name__ == "__main__": main() 

我不知道我怎麼能走的kwargs的flexibity的優勢,同時還採用了一些基本的繼承。在我寫的實際應用程序中,每個類實際上都有許多與其相關的不同屬性。

我擔心的是,因爲kwargs非常靈活(並且模糊不清),我不能保持我的課的親子關係 - data1data2data3data4都將得到應用到Child類。

如何確保data1data2Parent超類的屬性,並且data3data4Child子類的屬性?

我試圖設計我的類結構「正確的方式」,同時保持代碼有點簡單和優雅。它甚至重要嗎?也許我會在這個問題上走錯路。有一個更好的方法嗎?

對不起,如果我在這裏漫步。只是想明白。在此先感謝您的幫助和耐心......

+2

在Parent類的屬性和Child類的屬性之間進行區分是沒有意義的。整個繼承點 - 用任何語言 - 是一個孩子*是一個父母。 – 2014-10-06 20:11:41

回答

4

您的第一個問題是您實際上並沒有在第一時間創建屬性。如果你想要屬性,創建屬性。如果你想要一個鍵值映射,創建一個映射。你創建的是一個特殊目的,功能有限的非標準API映射。這沒有什麼好的理由。

如果你想確保的東西是什麼屬性,它必須是擺在首位的屬性,你通常與self.spam = eggs任務做(儘管在某些情況下,你setattr甚至self.__dict__.update做到這一點)。


如何確保數據1和數據2是父超的屬性,並且DATA3和DATA4分別兒童子類的屬性?

如果保證是類的不變量的一部分,你要明確確保每個類,並傳遞任何剩餘的參數了super。例如:

class Parent: 
    def __init__(self, **kwargs): 
     self.data1 = kwargs.pop('data1', None) 
     self.data2 = kwargs.pop('data2', None) 
     super().__init__(**kwargs) 

class Child(Parent): 
    def __init__(self, **kwargs): 
     self.data3 = kwargs.pop('data3', None) 
     self.data4 = kwargs.pop('data4', None) 
     super().__init__(**kwargs) 

但是,您可能會注意到,您並沒有真正從這種額外的普遍性中獲得任何好處;你可以和可能應─像這樣寫一樣的東西:

class Parent: 
    def __init__(self, *, data1=None, data2=None, **kwargs): 
     super().__init__(**kwargs) 
     self.data1 = data1 
     self.data2 = data2 

class Child(Parent): 
    def __init__(self, *, data3=None, data4=None, **kwargs): 
     super().__init__(**kwargs) 
     self.data3 = data3 
     self.data4 = data4 

之所以這麼說,我覺得你是從誤解工作擺在首位:

我值得關注的是,因爲kwargs非常靈活(和模糊),我無法保持我的類的父 - 子關係 - data1,data2,data3和data4將全部應用於Child類。

不,這些屬性都不適用於或者類。它們被應用於每個Child的實例 - 但是他們是否將這些屬性作爲Parent實例或Child實例獲得了這些屬性是沒有問題的;他們只是通過成爲__dict__並具有self.spam = eggs作業的對象得到它們。無論您將實例看作是Parent還是Child,這些屬性都存在 - 以及在構建之後添加很長時間的隨機附加屬性,或從object或其他任何其他屬性繼承的標準屬性。

如果你真的,真的要強制執行Parent情況不屬於Child情況下,只有datadata2,你可以做到這一點與__slots__@property,明確的描述符...讓我們做的第一,因爲它是最簡單和最清晰的位置:

class Parent: 
    __slots__ = ('data1', 'data2') 
    def __init__(self, **kwargs): 
     self.data1 = kwargs.pop('data1', None) 
     self.data2 = kwargs.pop('data2', None) 
     super().__init__(**kwargs) 

class Child(Parent): 
    __slots__ = ('data3', 'data4') 
    def __init__(self, **kwargs): 
     self.data3 = kwargs.pop('data3', None) 
     self.data4 = kwargs.pop('data4', None) 
     super().__init__(**kwargs) 

現在,任何試圖分配上Parent實例data3屬性將引發AttributeError,除非它實際上是一些子類,增加了一個data3插槽,像Child的一個實例。

+0

@dano:是的,錯字;感謝抓住它,現在修好了。 – abarnert 2014-10-06 19:58:33

+0

感謝您的全面反饋。我的主要目標是理解創建類和子類的正確方法(通常和從Python的角度來看);我很感激。 – drwtod 2014-10-06 21:07:40

+0

哦。是啊... *呃* ...我看到類和類的實例之間的區別。我在左邊的場地裏,那裏。 – drwtod 2014-10-06 21:08:42