2017-02-19 39 views
2

(!嘿,夥計們,這可能是一個很長的描述,請有一定的耐心,謝謝)蟒蛇 - 訪問父類的類屬性超().__的init __()

我有一個類Parent有許多實例屬性,我總是通過一個dict來初始化一個實例。就像這樣:

info = { 
    "name" : "Bob", 
    "col" : 5, 
    "line" : 10, 
    "type" : "Alien" 
} 

p = Parent(info) 

而在__init__方法我不想寫this.property_name = value每個屬性的Cuz的代碼會很長。例如:

class Parent(object): 

    def __init__(self, kwargs): 
     this.name = kwargs.get("name") 
     this.col = kwargs.get("col") 
     this.line = kwargs.get("line") 
     this.type = kwargs.get("type") 

所以我想用一個函數迭代dict設置這些實例屬性。這是我寫的函數:

def initialize_properties(instance, names, kwargs): 
    for name in names: 
     setattr(instance, name, kwargs.get(name)) 

看來,我需要存儲的屬性名稱列表names的地方,我決定把它保存爲一個類的屬性,因爲我希望我的班是人類友好 (當有人讀取類定義時,他知道這個類有什麼樣的屬性)。所以我改變了我的班級定義,如下所示:

class Parent(object): 
    props = ("name", "col", "line", "type") 

    def __init__(self, kwargs): 
     initialize_properties(self, self.props, kwargs) 

當不考慮繼承時,這可以正常工作。當我繼承Parent出現該問題:

class Child(Parent): 
    props = ("foo", "bar") 

    def __init__(self, kwargs): 
     super().__init__(kwargs) 
     initialize_properties(self, self.props, kwargs) 

我想要的Child實例在超Parent繼承所有實例屬性,與一些兒童專用的實例屬性,以及(這就是爲什麼我們使用繼承,不是嗎」所以我覆蓋類屬性props來定義特定於兒童的屬性。

但它不起作用。

info = { 
    "name" : "Bob", 
    "col" : 5, 
    "line" : 10, 
    "type" : "Alien", 
    "foo" : 5, 
    "bar" : 10 
} 

c = Child(info) 

c實例僅具有c.fooc.bar定義並設置,而沒有定義c.name

一些挖後,我發現,當Parent.__init__(self, kwargs)通過super()功能調用,self參數傳遞是Child類的,所以self.props評估爲Child.props

如果我想在Parent.props設置實例屬性,我必須在Parent.__init__(self, kwargs)明確地使用Parent.props,那就是:

class Parent(object): 
    props = ("name", "col", "line", "type") 

    def __init__(self, kwargs): 
     initialize_properties(self, Parent.props, kwargs) 

這將解決這個問題,但我認爲這不是很「乾淨」,因爲您必須對類名稱Parent進行硬編碼。

所以我的問題是:有沒有什麼辦法來檢測當前類並訪問它的類屬性,當你調用一個super().__init__()鏈來初始化一個子類實例?

+2

這似乎是很多工作,以避免'this.property_name = value'「爲每個屬性cuz代碼將很長」... –

回答

0

您可以實現收集來自所有supeclasses的props的方法,並在__init__使用它:

class Parent(object): 
    props = ("name", "col", "line", "type") 

    def __init__(self, **kwargs): 
     def __init__(self, kwargs): 
     initialize_properties(self, self.get_props(), kwargs) 

    def get_props(self): 
     return sum((getattr(k, 'props',()) for k in self.__class__.mro()),()) 

現在,你可以簡單地定義子類,你想要的方式。你甚至不必重寫構造:

class Child(Parent): 
    props = ("foo", "bar") 

話雖這麼說,你要求你希望你的類人類友好。如果人讀你的子類,他們會很樂意閱讀:

class Child(Parent): 
    props = 'foo', 'bar', 'name', 'col', 'line', 'type' 

,並擁有所有的類在一個地方props可用。

0

self.props首先引用實例屬性,然後是類屬性,父類的屬性,...

通過定義Child.propsself.props指的是Child.props class屬性,在那裏停止,不在父類中搜索。

如何使Child.props也包括父母的props

class Child(Parent): 
    props = Parent.props + ("foo", "bar") # <--- 

    def __init__(self, kwargs): 
     super().__init__(kwargs) 
     initialize_properties(self, self.props, kwargs) 
0

爲了回答您的問題

有什麼方法來檢測當前類和訪問它的類 屬性,當你調用超級鏈()。 init()

在我看來,沒有辦法。

但是,你可以用裝飾來簡化代碼

def add_props(names, cls=None): 
    if cls is None: 
     return lambda cls:add(names, cls) 
    for c in cls.__bases__: 
     names.extend(c.props) 
    names = list(set(names)) 
    cls.props = names 
    return cls 


class A: 
    props = ['a','b'] 


@add_props(names=['c']) 
class B(A): 
    pass 

輸出:

In [69]: B.props 
Out[69]: ['a', 'b', 'c'] 

但是,它必須說,如果你想對人友好的,最好的辦法,我認爲是沒有使用props,只寫代碼在你__init__方法。

class Root: 
    def __init__(self, a=None, b=None): 
     self.a = a 
     self.b = b 


class Child(Root): 
    def __init__(self, c=None, d=None, **kwargs): 
     self.c = c 
     self.d = d 
     super().__init__(**kwargs) 

您也可以方便的通過字典,只要你喜歡

data = {'a': 1} 
c = Child(**data) 

而且它不僅是友好的人,但也友好的大多數編輯器。