python 中具有相同成员和 super 的类的钻石继承
我发现自己处于钻石继承的奇怪情况,更糟糕的是钻石中间的类共享一个成员。下面我展示了一段精简的代码,突出显示了我的问题。
我在编写课程时使用的方法是从 http://rhettinger 学到的.wordpress.com/2011/05/26/super-considered-super/ 链接在 python 文档网站上,用于
class A(object):
pass
class B(A):
def __init__(self, z=None, **kwargs):
self.z = z
super(B, self).__init__(**kwargs)
# super(B, self).__init__(z=z, **kwargs)
class C(A):
def __init__(self, z=None, **kwargs):
self.z = z
super(C, self).__init__(**kwargs)
class D(B, C):
pass
d = D(z='y')
for arg, value in d.__dict__.iteritems():
print arg, ':', value
提供输出的
z : None
内置函数 super
问题是使用关键字参数来确保函数签名匹配的方法从类 C 的 init 调用中删除了 z 参数。我可以强制将参数添加回 kwargs (请参阅注释代码),但这会导致我无法实例化 B 类型的对象,因为这将导致调用不带参数的对象的 init ,这是一个很好的功能,因为它可以防止我在实例化以下任何对象时输入无效参数。
我还注意到,如果在设置变量之前有 super 行,那么问题就解决了,因为最高级别的对象将覆盖较低级别的对象。然而,我的代码主要是围绕最后的 super 构建的,因为所有实际的“设置”都是由低级别的类执行的,而较高级别的类则传递要在继承链中设置的值。是否有关于家长通话的位置的指南?
Stackoverflow 有什么想法吗?
谢谢
这只是“合作班级”的糟糕设计吗?到底什么是合作班?有什么规则或指导方针应该遵循吗?
编辑:
Stackoverflow 不会让我回答我自己的问题,但我想出了一个解决方案。
我认为合作类之间不应该有任何共享成员。如果有两个相互独立的类(从某种意义上说,不存在“是一种”关系)共享一个成员,那么您应该在继承关系中添加一个抽象级别。
共享成员应该被取出并放入一个单独的类中,该类继承自共享基类,并由菱形中的类继承。
下面是更正后的代码。为了与OP中的链接保持一致,我引入了一个Root基类,
我应该指出我没有接受过正式的编程教育(我猜像这里的大多数教育一样),所以如果我使用错误的术语,我深表歉意。
class Root(object):
pass
class A(Root):
pass
class HasAZ(Root):
def __init__(self, z=None, **kwargs):
self.z = z
super(HasAZ, self).__init__(**kwargs)
class B(HasAZ, A):
pass
class C(HasAZ, A):
pass
class D(B, C):
pass
d = D(z='y')
for arg, value in d.__dict__.iteritems():
print arg, ':', value
I find myself in the strange situation of diamond inheritance, even worse is that the classes in the middle of the diamond share a member. Below I've shown a cut down piece of code which highlights my problem.
The method I used in writing classes I have learnt from http://rhettinger.wordpress.com/2011/05/26/super-considered-super/ which is linked on the python documentation site for the built-in function super
class A(object):
pass
class B(A):
def __init__(self, z=None, **kwargs):
self.z = z
super(B, self).__init__(**kwargs)
# super(B, self).__init__(z=z, **kwargs)
class C(A):
def __init__(self, z=None, **kwargs):
self.z = z
super(C, self).__init__(**kwargs)
class D(B, C):
pass
d = D(z='y')
for arg, value in d.__dict__.iteritems():
print arg, ':', value
which gives output
z : None
The problem is that the method of using keyword arguments to ensure the signatures of the functions match takes away the z argument from class C's init call. I can forcibly add the argument back into kwargs (see commented code) but then this results in me not being able to instantiate an object of type B since this will result in a call to the init of object which takes no parameters, this is a nice feature because it prevents me putting in invalid arguments when instantiating any of the below object.
Also I noticed that if I have the line super before I set the variables then the problem is fixed since the highest level object will overwrite the lower levels. However my code is heavily built around have the super at the end since all the actual "setting" is carried out by the low ever level classes whilst the higher level classes pass the value to be set down the chain of inheritance. Are there guidelines on where to fit in parent calls?
Any ideas Stackoverflow?
thanks
p.s. Is this just a bad design of 'cooperative classes'? What is a cooperative class exactly?are there rules or guidelines one should follow?
edit:
Stackoverflow won't let me answer my own question but I came up with a solution.
I don't think there should be ever any shared members between cooperative classes. If ever there are two classes which are independent of each other (in the sense there is no 'is a kind of' relationship) which share a member then you should add a level of abstraction into the inheritance relationship.
The shared member should be taken out and put into a separate class which inherits from a shared base class and is inherited from by the classes in the diamond.
Below is the corrected code. In keeping with the link in the OP I introduced a Root base class
I should point out I have had no formal programming education (like most here I guess) so apologies if I use wrong terminology.
class Root(object):
pass
class A(Root):
pass
class HasAZ(Root):
def __init__(self, z=None, **kwargs):
self.z = z
super(HasAZ, self).__init__(**kwargs)
class B(HasAZ, A):
pass
class C(HasAZ, A):
pass
class D(B, C):
pass
d = D(z='y')
for arg, value in d.__dict__.iteritems():
print arg, ':', value
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
也许有更好的方法来做到这一点,但这应该避免覆盖::
Maybe there is a better way to do it but this should avoid the overwrite::
是的,特别是因为属性冲突。如果您没有同名的属性,那么到目前为止
Bz
不会被Cz
覆盖,这不会有问题。您已经读过的:
http://rhettinger.wordpress.com/ 2011/05/26/super-considered-super/
据我所知,这是合作班级规则/指南的最佳来源。
你可以做出选择。您可以:
,或者
__init__(self, **kwargs)
或者
Yes, specifically because of the attribute collision. If you didn't have the attribute the same name it wouldn't be a problem in so far a
B.z
wouldn't be overwritten byC.z
.The one you already read:
http://rhettinger.wordpress.com/2011/05/26/super-considered-super/
As far as I know that is the best source for the rules/guidelines for cooperative classes.
You have a choice to make. You can:
or
__init__(self, **kwargs)
or