【问题标题】:python - Accessing superclass's class attribute in super().__init__()python - 在 super().__init__() 中访问超类的类属性
【发布时间】:2017-02-19 03:31:54
【问题描述】:

我有一个类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没有定义。

经过一番挖掘,我发现当通过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__() 链来初始化子类实例时,有什么方法可以检测当前类并访问其类属性?

【问题讨论】:

  • 这似乎需要做很多工作来避免this.property_name = value“对于每个属性,因为代码会很长”...

标签: python class inheritance


【解决方案1】:

您可以实现一个方法,从所有超类中收集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】:

    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)
    

    【讨论】:

      【解决方案3】:

      回答你的问题

      有没有办法检测当前类并访问其类 属性,当您调用 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)
      

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

      【讨论】:

        猜你喜欢
        • 2021-04-25
        • 2011-06-16
        • 2015-02-18
        • 1970-01-01
        • 2013-03-24
        • 1970-01-01
        • 2017-03-04
        • 1970-01-01
        • 2019-05-26
        相关资源
        最近更新 更多