(嘿,伙计们,这可能是一个很长的描述,请耐心等一下,谢谢!)
我有一个具有许多实例属性的类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.foo
和c.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__()
链来初始化子类实例时,有没有办法检测当前类并访问其类属性?
答案 0 :(得分:1)
self.props
首先引用实例属性,然后是类属性,以及父类的属性,...
通过定义Child.props
,self.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)
它不仅对人类友好,而且对大多数编辑也很友好。