在super().__ init __()

时间:2017-02-19 03:31:54

标签: python class inheritance

(嘿,伙计们,这可能是一个很长的描述,请耐心等一下,谢谢!)

我有一个具有许多实例属性的类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来定义子特定属性。

但它没有用。

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.props中明确使用Parent.__init__(self, kwargs),即:

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

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

这样可以解决问题,但我认为它不是很清洁"因为你必须对类名Parent进行硬编码。

所以我的问题是:当您调用super().__init__()链来初始化子类实例时,有没有办法检测当前类并访问其类属性?

3 个答案:

答案 0 :(得分:1)

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)

答案 1 :(得分:0)

您可以实现一种方法,从所有supeclass中收集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

答案 2 :(得分:0)

回答你的问题

  

有没有办法检测当前类并访问其类   当你调用super()链时。 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)

你也可以随心所欲地传递dict

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

它不仅对人类友好,而且对大多数编辑也很友好。

相关问题