代码之家  ›  专栏  ›  技术社区  ›  Matt Joiner

隐式调用父类初始值设定项

  •  7
  • Matt Joiner  · 技术社区  · 15 年前
    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)
    

    在上面的代码中,注释掉了 __init__ 调用似乎是进行超级类初始化的常用“智能”方法。但是,在类层次结构可能发生更改的情况下,直到最近,我一直使用未注释的表单。

    在对的超级构造函数的调用中 B 在上面的层次结构中, B.__init__ 再次被调用, self.__class__ 实际上是 C 不是 就像我一直假设的那样。

    在python-2.x中,当调用超级构造函数而不命名当前类(即 super(B, self).__init__(1, b, c) )?

    4 回复  |  直到 8 年前
        1
  •  3
  •   Marius Gedminas    15 年前

    简短回答:不,没有办法隐式地调用权利 __init__ 在python 2.x中使用正确父类的正确参数。

    顺便说一下,这里显示的代码不正确:如果使用super()。 爱因斯坦 ,则层次结构中的所有类在它们的 爱因斯坦 方法。否则,如果引入一个使用多重继承的新子类,代码就可以停止工作。

    http://fuhm.net/super-harmful/ 更详细的问题描述(带图片)。

        2
  •  1
  •   Community CDub    7 年前

    您的代码与方法解析顺序无关。方法解析出现在多重继承的情况下,而不是您的示例。您的代码是错误的,因为您假定 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 zen的说法,因为代码的可读性较低,而且收益也很低,所以显式比隐式更好。但当心这一切 _init 方法有相同的参数,您不能完全忘记父级,我不建议这样做。

    对于单个继承,更好的方法是显式调用父级的方法,而不调用 super . 这样做你不必 命名当前类 但是你还是要关心谁是家长的班级。

    好的读数是: how-does-pythons-super-do-the-right-thing 以及在这个问题和特殊性中提出的联系 Python's Super is nifty, but you can't use it

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

    编辑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
    >>> 
    
        3
  •  1
  •   Qix - MONICA WAS MISTREATED    8 年前

    也许你要找的是元类?

    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的所有基类。

        4
  •  0
  •   Matt Anderson    15 年前

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

    给定类定义中的方法总是会破坏双下划线属性,以包括在其中定义的类的名称。因此,如果您将对名为mangled形式的类的引用存储在实例可以看到的地方,那么可以在调用 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 类对象本身的引用:

    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
    

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

    % 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>