【问题标题】:Is __init__ a class method?__init__ 是类方法吗?
【发布时间】:2015-04-19 02:03:56
【问题描述】:

我正在研究 Python 的超级方法和多重继承。我读到类似当我们使用 super 调用在所有基类中都有实现的基方法时,即使有各种参数,也只会调用一个类的方法。例如,

class Base1(object):
    def __init__(self, a):
        print "In Base 1"

class Base2(object):
    def __init__(self):
        print "In Base 2"

class Child(Base1, Base2):
    def __init__(self):
        super(Child, self).__init__('Intended for base 1')
        super(Child, self).__init__()# Intended for base 2

这将为第一个 super 方法生成 TyepErrorsuper 将调用它首先识别的任何方法实现并提供TypeError,而不是检查其他类。但是,当我们执行以下操作时,这将更加清晰并且可以正常工作:

class Child(Base1, Base2):
    def __init__(self):
        Base1.__init__(self, 'Intended for base 1')
        Base2.__init__(self) # Intended for base 2

这就引出了两个问题:

  1. __init__ 方法是静态方法还是类方法?
  2. 为什么要使用 super,它隐式地自行选择方法,而不是像后一个示例那样显式调用方法?对我来说,它看起来比使用 super 更干净。那么使用super比第二种方式有什么好处(除了用方法调用写基类名)

【问题讨论】:

  • 它是一个类方法(如果我理解正确,也就是实例方法)。静态没有 self 作为第一个参数。 Super 通常更好,因为您不需要知道基类的名称。
  • 但是如果我们要使用基类方法,最好知道我们调用的是哪个类方法,就多重继承而言,对吧?
  • 第二个代码不可能正常工作。另外,super() 不是这样工作的,调用 super 两次并不意味着它会自动调用 MRO 中的下一个类。
  • 这是一个特例。但是对于构造函数不带参数时的多重继承,super从left to right调用基类构造函数
  • 感谢@AshwiniChaudhary...更新了第二个代码的问题

标签: python python-2.7 inheritance multiple-inheritance


【解决方案1】:

super() 面对多重继承,尤其是在 object can get a bit tricky 上存在的方法上。一般规则是,如果您使用super,那么层次结构中的每个类都应该使用super。为__init__ 处理此问题的一个好方法是让每个方法都采用**kwargs,并始终在任何地方使用关键字参数。在调用object.__init__ 时,所有参数都应该已经弹出!

class Base1(object):
    def __init__(self, a, **kwargs):
        print "In Base 1", a
        super(Base1, self).__init__()

class Base2(object):
    def __init__(self, **kwargs):
        print "In Base 2"
        super(Base2, self).__init__()

class Child(Base1, Base2):
    def __init__(self, **kwargs):
        super(Child, self).__init__(a="Something for Base1")

查看链接的文章,了解更多关于它是如何工作的以及如何让它为你工作的方法!

编辑:冒着回答两个问题的风险,“为什么要使用 super?”

我们拥有super() 与我们拥有类和继承的许多相同原因,作为模块化和抽象代码的工具。在对类的实例进行操作时,您不需要知道该类是如何实现的所有细节,您只需要了解它的方法和属性,以及您打算如何使用该公共接口为班级。特别是,您可以确信,类实现的更改不会给您作为其实例的用户带来问题。

从基类派生新类型时,同样的论点也成立。您不想或不需要担心这些基类是如何实现的。这是一个具体示例,说明不使用 super 可能会出错。假设你有:

class Foo(object):
     def frob(self):
         print "frobbign as a foo"
class Bar(object):
     def frob(self):
         print "frobbign as a bar"

然后你创建一个子类:

class FooBar(Foo, Bar):
    def frob(self):
        Foo.frob(self)
        Bar.frob(self)

一切都很好,但是当你开始做的时候,你会意识到, Foo 真的是Bar 的一种,所以你改吧

 class Foo(Bar):
     def frob(self):
         print "frobbign as a foo"
         Bar.frob(self)

这一切都很好,除了在您的派生类中,FooBar.frob() 调用Bar.frob() 两次

这正是super() 解决的问题,它可以防止您多次调用超类实现(按指示使用时...)

【讨论】:

    【解决方案2】:

    至于你的第一个问题,__init__ 既不是静态方法也不是类方法;它是一个普通的实例方法。 (也就是说,它接收实例作为它的第一个参数。)

    至于你的第二个问题,如果你想显式调用多个基类实现,那么像你做的那样显式地这样做确实是唯一的方法。但是,您似乎误解了super 的工作原理。当您调用super 时,它不“知道”您是否已经调用它。您对super(Child, self).__init__ 的两次调用都调用了Base1 实现,因为那是“最近的父级”(Child 的最直接超类)。

    如果你想调用 just 这个直接的超类实现,你可以使用super。如果那个超类也被设置为调用它的超类,你会这样做,依此类推。使用super 的方法是让每个类只调用类层次结构中“向上”的下一个实现,以便super 调用的顺序总体上以正确的顺序调用需要调用的所有内容。这种设置通常被称为“合作继承”,你可以在网上找到各种关于它的文章,包括herehere

    【讨论】:

    • 但是在我的例子中,我们可以看到__init__即使没有创建实例也可以被调用。 Base2.__init__()。在这里,我们不是在创建 Base2 类的实例,对吧?
    • @thiruvenkadam:Child 的一个实例(即Child.__init__(self) 中的selfBase2 的一个实例!这就是成为子类的意义。
    • @thiruvenkadam:该代码实际上不起作用(正如您在编辑它后似乎已经注意到的那样)。
    • @IfLoop 谢谢...我错过了 :-)
    • @BrenBarn 是的...另一位用户指出了第二个代码的缺陷。修复了第二个问题并添加了一些关于我在问题中寻找的更多解释。
    猜你喜欢
    • 1970-01-01
    • 2015-11-27
    • 2017-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-26
    • 2017-08-28
    • 2011-08-03
    相关资源
    最近更新 更多