Pourquoi ne pas superclasse __init__ méthodes automatiquement invoquée?
Pourquoi le Python concepteurs de décider que les sous-classes " __init__()
méthodes ne sont pas automatiquement appeler le __init__()
méthodes de leurs super-classes, comme dans certaines autres langues? Est le Pythonic et recommandé idiome vraiment comme la suite?
class Superclass(object):
def __init__(self):
print 'Do something'
class Subclass(Superclass):
def __init__(self):
super(Subclass, self).__init__()
print 'Do something else'
- Vous pouvez écrire un décorateur d'hériter de la
__init__
méthode, et peut-être même de rechercher automatiquement les sous-classes et de les décorer. - sons vraiment une bonne idée. aimeriez-vous décrire un peu plus sur la décoratrice de la partie?
- oui décrire plus!
Vous devez vous connecter pour publier un commentaire.
La distinction cruciale entre Python
__init__
et ceux d'autres langues constructeurs est que__init__
est pas constructeur: c'est un initialiseur (le constructeur (si tout, mais, à voir plus tard;-) est__new__
et fonctionne complètement différemment de nouveau). Alors que la construction de toutes les super-classes (et, sans doute, de le faire "avant", vous permet de poursuivre la construction vers le bas) est évidemment de la partie pour dire que vous êtes la construction de une sous-classe de l'instance, qui n'est clairement pas le cas pour initialisation, car il existe de nombreux cas d'utilisation dans lequel les super-classes " initialisation doit être ignorée, modifié, contrôlée -- qui se passe, si, "au milieu" de la sous-classe d'initialisation, et ainsi de suite.Fondamentalement, super-classe de la délégation de l'initialiseur n'est pas automatique en Python, pour exactement les mêmes raisons, une telle délégation est pas automatique pour les tout autres méthodes-et notez que ces "autres langues" ne fais pas automatique super-classe de la délégation pour toute autre méthode, soit... juste pour le constructeur (et, le cas échéant, le destructeur), qui, comme je l'ai mentionné, est pas ce que Python
__init__
est. (Comportement de__new__
est également tout à fait particulière, mais vraiment pas liés directement à votre question, depuis__new__
est un étrange constructeur qu'il ne fait pas nécessairement besoin de construire quoi que ce soit, pourraient parfaitement bien retourner une instance existante, ou même un non-instance... clairement Python vous offre un beaucoup plus de contrôle de la mécanique que les "autres langues" que vous avez en tête, qui aussi comprend pas automatique délégation dans__new__
lui-même!-).__init__
n'est pas un constructeur... le véritable constructeur ... est__new__
". Comme vous-même,__new__
se comporte rien de tel qu'un constructeur à partir d'autres langues.__init__
est en fait très similaire (il est appelé lors de la création d'un nouvel objet, après cet objet est alloué, pour définir les variables de membre sur le nouvel objet), et est presque toujours de la place pour mettre en œuvre la fonctionnalité que dans les autres langues que vous mettriez dans un constructeur. Donc, il vous suffit d'appeler un constructeur!__new__
ne pas invoquer automatiquement superclasse__new__
soit. Afin que votre demande que la principale distinction est que construction implique nécessairement la construction de super-classes, tandis que initialisation n'est pas incompatible avec votre demande__new__
est le constructeur.__init__
docs.python.org/reference/datamodel.html#basic-customization : "spécial contrainte sur constructeurs, aucune valeur ne peut être retournés; cela risquerait de provoquer une erreur TypeError d'être soulevées lors de l'exécution" (l'emphase est mienne). Donc voilà, c'est officiel,__init__
est un constructeur.__init__
est appelé un constructeur. Ce constructeur est une fonction d'initialisation appelée après que l'objet a été entièrement construit et initialisé à l'état par défaut, y compris son dernier runtime type. Il n'est pas équivalent à C++ constructeurs, qui sont appelés sur une statiquement typé, objet alloué à un état indéfini. Ce sont aussi différents de__new__
, donc nous avons vraiment avoir au moins quatre différents types de répartition/construction/initialisation des fonctions. Langues utilise mixte de la terminologie, et l'important est le comportement, et non de la terminologie.Je suis un peu gêné quand les gens parrot le "Zen de Python", comme si c'est une justification pour quoi que ce soit. C'est une conception de la philosophie; en particulier, les décisions de conception peut toujours être expliqué en termes plus précis--et ils doivent être, ou encore de la "Zen de Python" devient une excuse pour faire n'importe quoi.
La raison en est simple: vous n'avez pas nécessairement construire une classe dérivée d'une manière similaire à la façon dont on construit la classe de base. Vous pouvez avoir plus de paramètres, de moins en moins, ils peuvent être dans un ordre différent ou non liés à tous.
Cela s'applique à tous les dérivées des méthodes, et pas seulement
__init__
.Java et C++ besoin qu'un constructeur de classe de base est appelé à cause de la disposition de la mémoire.
Si vous avez une classe
BaseClass
avec un membrefield1
, et vous créez une nouvelle classeSubClass
qui ajoute un membrefield2
, alors une instance deSubClass
contient un espace pourfield1
etfield2
. Vous avez besoin d'un constructeur deBaseClass
à remplirfield1
, sauf si vous avez besoin de toutes les classes héritant de répéterBaseClass
s'initialisation dans leurs propres constructeurs. Et sifield1
est privé, alors hériter de classes ne peut pas initialiserfield1
.Python n'est pas du Java ou du C++. Toutes les instances de toutes les classes définies par l'utilisateur ont la même "forme". Ils sont fondamentalement juste dictionnaires dans lesquels les attributs peuvent être insérés. Avant tout, l'initialisation a été fait, toutes les instances de toutes les classes définies par l'utilisateur sont presque exactement la même, elles sont simplement des endroits pour stocker les attributs qui ne sont pas de stocker tout encore.
Il est donc parfaitement logique pour un Python sous-classe de ne pas appeler son constructeur de classe de base. Il suffirait d'ajouter les attributs d'elle-même si elle le voulait. Il n'y a pas d'espace réservé pour un certain nombre de champs pour chaque classe dans la hiérarchie, et il n'y a pas de différence entre un attribut ajouté par code à partir d'un
BaseClass
méthode et un attribut ajouté par code à partir d'unSubClass
méthode.Si, comme il est courant,
SubClass
réellement ne voulez avoir tousBaseClass
's invariants mis en place avant, il va faire de son propre personnalisation, alors oui vous pouvez les appelerBaseClass.__init__()
(ou utilisezsuper
, mais c'est compliqué et a ses propres problèmes, parfois). Mais vous n'avez pas à. Et vous pouvez le faire avant, ou après, ou avec des arguments différents. L'enfer, si vous vouliez vous pourriez appeler laBaseClass.__init__
à partir d'une autre méthode entièrement de__init__
; peut-être que vous avez une étrange initialisation de chose.Python réalise cette flexibilité en gardant les choses simples. Vous permet d'initialiser les objets par la rédaction d'un
__init__
méthode qui définit les attributs desself
. C'est tout. Il se comporte exactement comme une méthode, parce que c'est exactement une méthode. Il n'y a pas d'autres étranges et peu intuitive des règles au sujet de choses à faire en premier, ou des choses qui se déclencheront automatiquement si vous n'avez pas à faire d'autres choses. Le seul but qu'il doit servir, c'est être un crochet à exécuter au cours de l'initialisation des objets à l'ensemble initial de valeurs d'attribut, et c'est exactement ce qu'. Si vous voulez faire autre chose, de vous écrire explicitement que dans votre code."Explicite est mieux qu'implicites." C'est le même raisonnement qui indique que nous devrions écrire explicitement "moi".
Je pense que dans la fin, c'est un avantage-- pouvez-vous réciter toutes les règles de Java a concernant l'appel de super-classes des constructeurs?
Souvent de la sous-classe a d'autres paramètres qui ne peuvent pas être transmis à la super-classe.
Droit maintenant, nous avons une assez longue page décrivant la méthode de résolution de l'ordre en cas d'héritage multiple: http://www.python.org/download/releases/2.3/mro/
Si les constructeurs ont appelé automatiquement, vous auriez besoin d'une autre page au moins la même longueur d'expliquer l'ordre de ce qui se passe. Que serait l'enfer...
Pour éviter toute confusion, il est utile de savoir que vous pouvez invoquer le base_class
__init__()
méthode si le child_class ne dispose pas d'un__init__()
classe.Exemple:
En fait, le MRO en python va chercher
__init__()
dans la classe parent lorsqu'ne pouvez pas le trouver dans la classe des enfants. Vous devez invoquer le constructeur de la classe parent directement si vous avez déjà un__init__()
méthode dans la classe des enfants.Par exemple le code suivant renvoie une erreur:
la classe parent:
def init(self, a=1, b=0):
auto.a = a
auto.b = b
Peut-être
__init__
est la méthode de la sous-classe doit remplacer. Parfois, les sous-classes ont besoin de la mère de la fonction à exécuter avant d'ajouter une classe de code spécifiques, et d'autres fois ils ont besoin pour configurer les variables d'instance avant l'appel de la mère de la fonction. Car il n'y a pas de façon Python pourrait savoir quand il serait plus approprié d'appeler ces fonctions, il ne devrait pas le deviner.Si ce n'est pas vous balancer, considèrent que
__init__
est Juste une Autre Fonction. Si la fonction en question ont étédostuff
au lieu de cela, voulez-vous encore envie de Python pour appeler automatiquement la fonction correspondante dans la classe parent?je crois que l'une considération très importante ici est que, avec un appel automatique à
super.__init__()
, vous proscrire, par conception, lorsque que l'initialisation de la méthode est appelée, et avec quels arguments. évitant automatiquement l'appelant, et en exigeant que le programmeur explicitement de faire appel, nécessite beaucoup de souplesse.après tout, juste parce que la classe B est dérivée de la classe A ne signifie pas
A.__init__()
peut ou doit être appelée avec les mêmes arguments queB.__init__()
. de faire l'appel explicite signifie un programmeur peut avoir par exemple définirB.__init__()
à fait différents paramètres, faire un peu de calcul avec les données, appelA.__init__()
avec des arguments de cette méthode, et puis faire un peu de post-traitement. ce type de flexibilité serait difficile à atteindre siA.__init__()
serait appelée à partir deB.__init__()
implicitement, que ce soit avantB.__init__()
exécute ou juste après.