Если вы реализуете __init__
в классе, производном от BaseClass, он перезапишет унаследованный метод __init__
и, следовательно, BaseClass.__init__
никогда не будет вызван. Если вам нужно вызвать метод __init__
для BaseClass (как это обычно бывает), то вам нужно сделать это, и это делается явно, вызывая 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__
также был перезаписан. Мы обходим это, явно вызывая Foo.__init__
из Bar.__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 (который в одиночном наследовании является родительским классом). См. документацию по super для получения дополнительной информации.