如果您在派生自 BaseClass 的类中实现 __init__,那么它将覆盖继承的 __init__ 方法,因此永远不会调用 BaseClass.__init__。如果您需要为 BaseClass 调用 __init__ 方法(通常是这种情况),那么由您来执行此操作,并通过调用 BaseClass.__init__ 显式完成,通常从新实现的 __init__ 方法中完成。
class Foo(object):
def __init__(self):
self.a = 10
def do_something(self):
print self.a
class Bar(Foo):
def __init__(self):
self.b = 20
bar = Bar()
bar.do_something()
这将导致以下错误:
AttributeError: 'Bar' object has no attribute 'a'
因此,do_something 方法已按预期继承,但该方法需要设置属性 a,但它从来没有设置,因为 __init__ 也被覆盖了。我们通过在Bar.__init__ 中显式调用Foo.__init__ 来解决这个问题。
class Foo(object):
def __init__(self):
self.a = 10
def do_something(self):
print self.a
class Bar(Foo):
def __init__(self):
Foo.__init__(self)
self.b = 20
bar = Bar()
bar.do_something()
按预期打印10。在这种情况下,Foo.__init__ 需要一个单独的参数,它是 Foo 的一个实例(按照惯例称为 self)。
通常,当您在类的实例上调用方法时,类实例会自动作为第一个参数传递。类实例上的方法称为绑定方法。 bar.do_something 是绑定方法的一个示例(您会注意到它是在没有任何参数的情况下调用的)。 Foo.__init__ 是一个未绑定的方法,因为它没有附加到Foo 的特定实例,所以第一个参数,Foo 的实例,需要显式传递。
在我们的例子中,我们将self 传递给Foo.__init__,这是传递给Bar 中__init__ 方法的Bar 的实例。由于Bar 继承自Foo,Bar 的实例也是Foo 的实例,因此允许将self 传递给Foo.__init__。
您所继承的类很可能需要或接受更多参数,而不仅仅是类的实例。这些处理方式与您在 __init__ 中调用的任何方法一样:
class Foo(object):
def __init__(self, a=10):
self.a = a
def do_something(self):
print self.a
class Bar(Foo):
def __init__(self):
Foo.__init__(self, 20)
bar = Bar()
bar.do_something()
这将打印20。
如果您尝试实现一个接口,该接口通过您的继承类完全公开基类的所有初始化参数,您需要明确地这样做。这通常使用 *args 和 **kwargs 参数(名称按约定)完成,它们是所有未明确命名的其余参数的占位符。以下示例利用了我讨论过的所有内容:
class Foo(object):
def __init__(self, a, b=10):
self.num = a * b
def do_something(self):
print self.num
class Bar(Foo):
def __init__(self, c=20, *args, **kwargs):
Foo.__init__(self, *args, **kwargs)
self.c = c
def do_something(self):
Foo.do_something(self)
print self.c
bar = Bar(40, a=15)
bar.do_something()
在这种情况下,参数 c 设置为 40,因为它是 Bar.__init__ 的第一个参数。然后将第二个参数合并到变量 args 和 kwargs 中(* 和 ** 是特定语法,表示在传递给函数/方法时将列表/元组或字典扩展为单独的参数),并被传递到Foo.__init__。
此示例还指出,如果需要,任何覆盖的方法都需要显式调用(如do_something在这种情况下)。
最后一点,您经常会看到使用super(ChildClass, self).method()(其中ChildClass 是某个任意子类)而不是显式调用BaseClass 方法。对super 的讨论完全是另一个问题,但我只想说,在这些情况下,它通常用于通过调用BaseClass.method(self) 来执行正在执行的操作。简而言之,super 将方法调用委托给方法解析顺序中的下一个类 - MRO(在单继承中是父类)。请参阅documentation on super 了解更多信息。