Il ne semble pas y avoir de bonne documentation en ligne à ce sujet: si je crée une classe dérivée, aura-t-elle automatiquement tous les attributs de la classe de base? Mais à quoi sert la BaseClass.__init()
, devez-vous également la faire avec d'autres méthodes de classe de base? BaseClass.__init__()
a-t-il besoin d'arguments? Si vous avez des arguments pour votre classe de base __init__()
, sont-ils également utilisés par la classe dérivée, devez-vous définir explicitement les arguments sur la __init__()
de la classe dérivée, ou les définir sur BaseClass.__init__()
à la place?
Si vous implémentez __init__
Dans une classe dérivée de BaseClass, il écrasera la méthode héritée __init__
Et ainsi BaseClass.__init__
Ne sera jamais appelé. Si vous devez appeler la méthode __init__
Pour BaseClass (comme c'est normalement le cas), alors c'est à vous de le faire, et c'est fait explicitement en appelant BaseClass.__init__
, Normalement à partir du nouveau implémenté la méthode __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()
Cela provoquera l'erreur suivante:
AttributeError: 'Bar' object has no attribute 'a'
Ainsi, la méthode do_something
A été héritée comme prévu, mais cette méthode nécessite que l'attribut a
ait été défini, ce qu'il n'est jamais parce que __init__
A également été remplacé. Nous contournons cela en appelant explicitement Foo.__init__
À partir de 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()
qui imprime 10
comme prévu. Foo.__init__
Dans ce cas attend un seul argument qui est une instance de Foo
(qui par convention est appelée self
).
Normalement, lorsque vous appelez une méthode sur une instance d'une classe, l'instance de classe est transmise automatiquement comme premier argument. Les méthodes sur une instance d'une classe sont appelées méthodes liées . bar.do_something
Est un exemple de méthode liée (et vous remarquerez qu'elle est appelée sans aucun argument). Foo.__init__
Est une méthode non liée car elle n'est pas attachée à une instance particulière de Foo
, donc le premier argument, une instance de Foo
, doit être passée explicitement.
Dans notre cas, nous passons self
à Foo.__init__
, Qui est l'instance de Bar
qui a été passée à la méthode __init__
Dans Bar
. Puisque Bar
hérite de Foo
, les instances de Bar
sont également des instances de Foo
, donc en passant self
à Foo.__init__
est autorisée.
Il est probable que la classe dont vous héritez requiert ou accepte plus d'arguments qu'une simple instance de la classe. Ils sont traités comme vous le feriez avec n'importe quelle méthode que vous appelez à partir de __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()
qui afficherait 20
.
Si vous essayez d'implémenter une interface qui expose entièrement tous les arguments d'initialisation de la classe de base via votre classe héritée, vous devrez le faire explicitement. Cela se fait généralement avec les arguments * args et ** kwargs (les noms sont par convention), qui sont des espaces réservés pour tous les autres arguments qui ne sont pas explicitement nommés. L'exemple suivant utilise tout ce dont j'ai discuté:
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()
Dans ce cas, l'argument c
est défini sur 40, car il s'agit du premier argument de Bar.__init__
. Le deuxième argument est ensuite incorporé dans les variables args
et kwargs
(le * et ** est une syntaxe spécifique qui dit développer la liste/Tuple ou le dictionnaire en arguments séparés lors du passage à une fonction/méthode ), et est transmis à Foo.__init__
.
Cet exemple montre également que toute méthode écrasée doit être appelée explicitement si c'est ce qui est requis (comme do_something
Est dans ce Cas).
Un dernier point, vous verrez souvent super(ChildClass, self).method()
(où ChildClass
est une classe enfant arbitraire) utilisée au lieu d'un appel à la méthode BaseClass
explicitement. La discussion de super
est une toute autre question, mais il suffit de dire que, dans ces cas, il est généralement utilisé pour faire exactement ce qui est fait en appelant BaseClass.method(self)
. En bref, super
délègue l'appel de méthode à la classe suivante dans l'ordre de résolution de la méthode - le MRO (qui en héritage unique est la classe parente). Voir documentation sur super pour plus d'informations.
Si je crée une classe dérivée, aura-t-elle automatiquement tous les attributs de la classe de base?
Attributs de classe, oui. Attributs d'instance, non (simplement parce qu'ils n'existent pas lors de la création de la classe), sauf s'il n'y a pas de __init__
dans la classe dérivée, auquel cas la base sera appelée à la place et définira les attributs d'instance.
BaseClass .init () a-t-il besoin d'arguments?
Dépend de la classe et de son __init__
Signature. Si vous appelez explicitement Base.__init__
dans la classe dérivée, vous devrez au moins passer self
comme premier argument. Si tu as
class Base(object):
def __init__(self):
# something
alors il est assez évident qu'aucun autre argument n'est accepté par le __init__
. Si vous aviez
class Base(object):
def __init__(self, argument):
# something
alors vous devez passer argument
lorsque vous appelez base __init__
. Pas de science fusée ici.
Si vous avez des arguments pour votre classe de base init (), sont-ils également utilisés par la classe dérivée, devez-vous définir explicitement les arguments sur la classe dérivée init () ou définissez-les sur BaseClass .init () à la place?
Encore une fois, si la classe dérivée n'a pas __init__
, la base 1 sera utilisée à la place.
class Base(object):
def __init__(self, foo):
print 'Base'
class Derived(Base):
pass
Derived() # TypeError
Derived(42) # prints Base
Dans les autres cas, vous devez en prendre soin d'une manière ou d'une autre. Que vous utilisiez *args, **kwargs
et simplement passer des arguments non modifiés à la classe de base, ou copier la signature de la classe de base, ou fournir des arguments ailleurs, dépend de ce que vous essayez d'accomplir.