7
class A(object): 
    def __init__(self, a, b, c): 
     #super(A, self).__init__() 
     super(self.__class__, self).__init__() 


class B(A): 
    def __init__(self, b, c): 
     print super(B, self) 
     print super(self.__class__, self) 
     #super(B, self).__init__(1, b, c) 
     super(self.__class__, self).__init__(1, b, c) 

class C(B): 
    def __init__(self, c): 
     #super(C, self).__init__(2, c) 
     super(self.__class__, self).__init__(2, c) 
C(3) 

在上面的代码中,注释掉的调用看起来是普遍接受的“智能”方式来执行超类初始化。但是,如果类层次结构可能发生变化,我一直在使用未注释的表单,直到最近。隐式调用父类初始化器

看来,在上述层次结构中的调用超级构造函数B,即B.__init__再次调用,self.__class__实际上是C,不B因为我一直以为。

是否有Python的2.x的某种方式,我可以保持适当的MRO(相对于初始化以正确的顺序所有父类)调用超构造函数时,而不是在super(B, self).__init__(1, b, c)命名当前类(B在)?

回答

3

简短的回答:没有,没有办法隐含调用正确__init__与正确的父类的在Python 2.x的正确参数

顺便说一句,这里显示的代码是不正确的:如果你使用super()。 __init__,那么层次结构中的所有类必须在它们的__init__方法中具有相同的签名。否则,如果您引入使用多重继承的新子类,则代码可能会停止工作。

请参阅http://fuhm.net/super-harmful/了解更多问题描述(附图)。

+0

我开始怀疑初始签名和多重继承 – 2010-05-04 03:14:34

1

也许你在找什么是元类?

class metawrap(type): 
    def __new__(mcs,name, bases, dict): 
     dict['bases'] = bases 
     return type.__new__(mcs,name,bases,dict) 

class A(object): 
    def __init__(self): 
     pass 
    def test(self): 
     print "I am class A" 

class B(A): 
    __metaclass__ = metawrap 
    def __init__(self): 
     pass 
    def test(self): 
     par = super(self.bases[0],self) 
     par.__thisclass__.test(self) 
foo = B() 
foo.test() 

版画“我是A级”

什么元类确实是压倒一切的B级(不是对象)的初始创建并确保内置的字典每个B对象现在包含一个碱基阵列,您可以在其中找到B的所有碱基类别

+0

我刚刚意识到我正在用大炮(丹麦谚语)射击麻雀,为此我表示歉意,但我会让这一点成为现实,除非有人决定删除它。 – 2010-03-01 10:35:50

+0

我宁愿不诉诸元类的东西,应该是微不足道的,但谢谢 – 2010-04-30 20:27:34

1

您的代码与方法解析顺序无关。方法解决来自多重继承,这不是你的例子。您的代码是完全错误的,因为你认为self.__class__实际上是同一类的一个地方被定义的方法,这是错误的:

>>> class A(object): 
...  def __init__(self): 
...   print self.__class__ 
... 
>>> 
>>> class B(A): 
...  def __init__(self): 
...   A.__init__(self) 
... 
>>> B() 
<class '__main__.B'> 
<__main__.B object at 0x1bcfed0> 
>>> A() 
<class '__main__.A'> 
<__main__.A object at 0x1bcff90> 
>>> 

所以当你要拨打:

super(B, self).__init__(1, b, c) 

你的确呼吁:

# super(self.__class__, self).__init__(1, b, c) 
super(C, self).__init__(1, b, c) 

编辑:试图更好地回答这个问题。

class A(object): 
    def __init__(self, a): 
     for cls in self.__class__.mro(): 
      if cls is not object: 
       cls._init(self, a) 
    def _init(self, a): 
     print 'A._init' 
     self.a = a 

class B(A): 
    def _init(self, a): 
     print 'B._init' 

class C(A): 
    def _init(self, a): 
     print 'C._init' 

class D(B, C): 
    def _init(self, a): 
     print 'D._init' 


d = D(3) 
print d.a 

打印:

D._init 
B._init 
C._init 
A._init 
3 

(的template pattern修改版本)。

现在,父母的方法实际上被隐含地称为,但我必须同意python禅,其中显式优于隐式,因为代码是较少可读性和增益较差。但要注意,所有_init方法都有相同的参数,您不能完全忘记父母,我不建议这样做。

对于单继承,更好的方法是显式调用父类的方法,而不调用super。这样做你不必命名当前类,但你仍然必须关心谁是父母的类。

好读是:how-does-pythons-super-do-the-right-thing在这个问题建议的链接和特殊性Python's Super is nifty, but you can't use it

如果层次结构可能会发生改变是糟糕的设计的症状,并在所有的部件后果谁正在使用的代码和不应该受到鼓励。

EDIT 2

另一个例子是我的想法,但它使用元类。 Urwid库uses metaclass在类中存储属性__super,以便您只需访问该属性即可。

例:

>>> class MetaSuper(type): 
...  """adding .__super""" 
...  def __init__(cls, name, bases, d): 
...   super(MetaSuper, cls).__init__(name, bases, d) 
...   if hasattr(cls, "_%s__super" % name): 
...    raise AttributeError, "Class has same name as one of its super classes" 
...   setattr(cls, "_%s__super" % name, super(cls)) 
... 
>>> class A: 
... __metaclass__ = MetaSuper 
... def __init__(self, a): 
... self.a = a 
... print 'A.__init__' 
... 
>>> class B(A): 
... def __init__(self, a): 
... print 'B.__init__' 
... self.__super.__init__(a) 
... 
>>> b = B(42) 
B.__init__ 
A.__init__ 
>>> b.a 
42 
>>> 
0

据我所知,以下不常用。但它似乎工作。

给定类定义中的方法总是破坏双下划线属性以包含它们所定义的类的名称。因此,如果您以名称改变的形式存储对实例可以看到的类的引用,您可以在拨打super时使用该功能。

一个例子积攒对象本身的引用,通过在基类实现__new__

def mangle(cls, name): 
    if not name.startswith('__'): 
     raise ValueError('name must start with double underscore') 
    return '_%s%s' % (cls.__name__, name) 

class ClassStasher(object): 
    def __new__(cls, *args, **kwargs): 
     obj = object.__new__(cls) 
     for c in cls.mro(): 
      setattr(obj, mangle(c, '__class'), c) 
     return obj 

class A(ClassStasher): 
    def __init__(self): 
     print 'init in A', self.__class 
     super(self.__class, self).__init__() 

class B(A): 
    def __init__(self): 
     print 'init in B', self.__class 
     super(self.__class, self).__init__() 

class C(A): 
    def __init__(self): 
     print 'init in C', self.__class 
     super(self.__class, self).__init__() 

class D(B, C): 
    def __init__(self): 
     print 'init in D', self.__class 
     super(self.__class, self).__init__() 


d = D()  
print d 

而且,在做类似的事情,但使用一元类和积攒的类对象的__class引用自己:

运行所有这些组合起来,作为一个源文件:

% python /tmp/junk.py 
init in D <class '__main__.D'> 
init in B <class '__main__.B'> 
init in C <class '__main__.C'> 
init in A <class '__main__.A'> 
<__main__.D object at 0x1004a4a50> 
init in D_meta <class '__main__.D_meta'> 
init in B_meta <class '__main__.B_meta'> 
init in C_meta <class '__main__.C_meta'> 
init in A_meta <class '__main__.A_meta'> 
<__main__.D_meta object at 0x1004a4bd0>