簡體   English   中英

python - 在 super().__init__() 中訪問超類的類屬性

[英]python - Accessing superclass's class attribute in super().__init__()

我有一個具有許多實例屬性的Parent類,我總是傳遞一個dict來初始化一個實例。 像這樣:

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

p = Parent(info)

__init__方法中,我不想為每個屬性編寫this.property_name = value因為代碼會很長。 例如:

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來定義 child-具體屬性。

但它不起作用。

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

c = Child(info)

實例c只有c.fooc.bar定義和設置,而c.name沒有定義。

經過一番挖掘,我發現當通過super()函數調用Parent.__init__(self, kwargs) ,傳遞的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__()鏈來初始化子類實例時,有沒有辦法檢測當前類並訪問其類屬性?

您可以實現一個從所有超類收集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集中在一處。

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

通過定義Child.propsself.props引用Child.props類屬性,停在那里,而不是在父類中搜索。

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

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

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

回答你的問題

當您調用 super() 鏈時,有什么方法可以檢測當前類並訪問其類屬性。 初始化()

在我看來,沒辦法。

但是,您可以使用裝飾器來簡化您的代碼

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)

您也可以方便地根據需要傳遞 dict

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

它不僅對人類友好,而且對大多數編輯也友好。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM