关于python:隐式调用父类初始值设定项

Implicitly invoking parent class initializer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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)

在上面的代码,commented出__init__保证金的appear两种accepted是"聪明"的方式做超类的初始化。然而,在这类事件,层冰可能已经改变,以注释的形式,直到最近。

它出现在这两个超级constructor呼叫为B以上层次的,这就是所谓的B.__init__冰的了,我Cself.__class__冰槽,BAA assumed永远的仇恨。

有一些的方式在Python 2。x,我可以保持良好的()(私营initializing登两类所有的父母在正确的时候打电话给超阶)命名的交底不随流类(《B体内super(B, self).__init__(1, b, c))?


简短回答:不,没有办法用Python2.x中正确父类的正确参数隐式调用右边的__init__

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

请参阅http://fuhm.net/super-harmable/了解有关该问题的详细描述(带图片)。


您的代码与方法解析顺序无关。方法解析出现在多重继承的情况下,而不是您的示例。您的代码是完全错误的,因为您假定self.__class__实际上是定义方法的同一类,而这是错误的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> 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>
>>>

所以当你应该打电话给:

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

你确实在打电话:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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

印刷品:

1
2
3
4
5
D._init
B._init
C._init
A._init
3

(模板模式的修改版本)。

现在父母的方法实际上是隐式调用的,但是我必须同意python zen的说法,因为代码的可读性较低,而且收益也很低,所以显式比隐式更好。但是要注意,所有_init方法都有相同的参数,你不能完全忘记父母,我不建议这样做。

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

好的读物是:pythons super是如何做正确的事情的,这个问题中建议的链接,特别是python的super很漂亮,但是你不能使用它。

如果层次结构可能发生变化,则是不良设计的症状,并且会对使用该代码的所有部分产生影响,因此不应鼓励使用该代码。

编辑2

我想到了另一个例子,但它使用元类。URWID库使用元类在类中存储属性__super,这样您只需访问该属性即可。

前任:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
>>> 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
>>>


也许你要找的是元类?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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()

打印"我是甲级学生"

元类所做的是覆盖B类(而不是对象)的初始创建,并确保每个B对象的内置字典现在包含一个基数组,您可以在其中查找B的所有基类。


据我所知,以下工作并不常见。但它似乎确实有效。

给定类定义中的方法总是会破坏双下划线属性,以包括在其中定义的类的名称。因此,如果您在实例可以看到的地方以名称损坏的形式存储对类的引用,那么可以在对super的调用中使用它。

通过在基类上实现__new__,将引用存储在对象本身上的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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引用存储在类对象本身上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class ClassStasherType(type):
    def __init__(cls, name, bases, attributes):
        setattr(cls, mangle(cls, '__class'), cls)

class ClassStasher(object):
    __metaclass__ = ClassStasherType

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

class B_meta(A_meta):
    def __init__(self):
        print 'init in B_meta', self.__class
        super(self.__class, self).__init__()

class C_meta(A_meta):
    def __init__(self):
        print 'init in C_meta', self.__class
        super(self.__class, self).__init__()

class D_meta(B_meta, C_meta):
    def __init__(self):
        print 'init in D_meta', self.__class
        super(self.__class, self).__init__()


d = D_meta()    
print d

同时运行这些文件,作为一个源文件:

1
2
3
4
5
6
7
8
9
10
11
% 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>