97 votes

Comprendre __init_subclass__

J'ai enfin mis à jour ma version de python et j'ai été découvrir les nouvelles fonctionnalités ajoutées. Entre autres choses, j'ai été de me gratter la tête autour de la nouvelle - __init_subclass__ méthode. À partir de la documentation:

Cette méthode est appelée à chaque fois que le contenant de la classe est sous-classé. cls est alors la nouvelle sous-classe. Si elle est définie comme une instance normale de la méthode, ce la méthode est implicitement converti à une méthode de classe.

J'ai donc commencé à jouer avec elle un peu, en suivant l'exemple de la doc:

class Philosopher:
    def __init_subclass__(cls, default_name, **kwargs):
        super().__init_subclass__(**kwargs)
        print(f"Called __init_subclass({cls}, {default_name})")
        cls.default_name = default_name

class AustralianPhilosopher(Philosopher, default_name="Bruce"):
    pass

class GermanPhilosopher(Philosopher, default_name="Nietzsche"):
    default_name = "Hegel"
    print("Set name to Hegel")

Bruce = AustralianPhilosopher()
Mistery = GermanPhilosopher()
print(Bruce.default_name)
print(Mistery.default_name)

Produit de cette sortie:

Called __init_subclass(<class '__main__.AustralianPhilosopher'>, 'Bruce')
'Set name to Hegel'
Called __init_subclass(<class '__main__.GermanPhilosopher'>, 'Nietzsche')
'Bruce'
'Nietzsche'

Je comprends que cette méthode est appelée après la définition de sous-classe, mais mes questions sont particulièrement à propos de l'utilisation de cette fonctionnalité. J'ai lu le PEP 487 article, mais ne m'aide pas beaucoup. Où serait cette méthode-il être utile? Est-ce pour:

  • la super-classe d'enregistrer les sous-classes lors de la création?
  • forcer la sous-classe pour définir un champ à la définition du temps?

Aussi, dois-je comprendre l' __set_name__ pour bien comprendre son utilisation?

81voto

Martijn Pieters Points 271458

PEP 487 dans deux communes de la métaclasse usecases et les rendre plus accessibles, sans avoir à comprendre tous les tenants et les aboutissants de metaclasses. Les deux nouvelles fonctionnalités, __init_subclass__ et __set_name__ sont par ailleurs indépendants, ils ne dépendent pas l'un de l'autre.

__init_subclass__ n'est qu'un crochet de la méthode. Vous pouvez l'utiliser pour tout ce que vous voulez. Il est utile à la fois pour l'enregistrement de sous-classes d'une certaine façon, et pour le réglage des valeurs d'attribut par défaut sur les sous-classes.

Nous avons récemment utilisé cette fonction pour fournir des 'cartes' pour les différents systèmes de contrôle de version, par exemple:

class RepositoryType(Enum):
    HG = auto()
    GIT = auto()
    SVN = auto()
    PERFORCE = auto()

class Repository():
    _registry = {t: {} for t in RepositoryType}

    def __init_subclass__(cls, scm_type=None, name=None, **kwargs):
        super().__init_subclass__(**kwargs)
        if scm_type is not None:
            cls._registry[scm_type][name] = cls

class MainHgRepository(Repository, scm_type=RepositoryType.HG, name='main'):
    pass

class GenericGitRepository(Repository, scm_type=RepositoryType.GIT):
    pass

Cette trivialement nous permet de définir les classes de gestionnaire pour des référentiels spécifiques, sans avoir à recourir à l'aide d'une métaclasse ou des décorateurs.

51voto

Antti Haapala Points 11542

__init_subclass__ et __set_name__ sont orthogonaux mécanismes - ils ne sont pas liés les uns aux autres, vient d'être décrite de la même CRÊTE. Les deux sont des fonctionnalités qui avait besoin d'un complet métaclasse avant. Le PEP 487 adresses 2 de la plupart des utilisations courantes de metaclasses:

  • comment permettre aux parents de savoir quand il est sous-classé (__init_subclass__)
  • comment permettre à un descripteur de classe de connaître le nom de la propriété, il est utilisé pour (__set_name__)

Comme le PPE-tse dit:

Bien qu'il existe de nombreuses façons d'utiliser une métaclasse, la grande majorité des cas d'utilisation tombe justement dans trois catégories: les unes code d'initialisation en cours d'exécution après la création de classes, l'initialisation de descripteurs et de maintien de l'ordre dans lequel les attributs de classe ont été définis.

Les deux premières catégories peuvent facilement être réalisé en ayant des crochets simples dans la création de la classe:

  • Un __init_subclass__ crochet qui initialise toutes les sous-classes d'une classe donnée.
  • lors de la création de classes, une __set_name__ hook est appelé sur tous les attribut (descripteurs) définie dans la classe, et

La troisième catégorie est le sujet d'un autre PEP, PEP 520.

Remarquez aussi que, tandis que __init_subclass__ est un remplacement pour l'utilisation d'une métaclasse dans cette classe de l'arbre d'héritage, __set_name__ dans un descripteur de classe est un remplacement pour l'utilisation d'une métaclasse pour la classe qui a une instance de la descripteur comme un attribut.

8voto

Jim Points 8793

Le principal point d' __init_subclass__ a été, comme le titre de la PEP suggèrent, pour offrir une forme plus simple de personnalisation pour les classes.

C'est un crochet qui permet de bricoler avec des classes w/o le besoin de savoir sur les metaclasses, de garder trace de tous les aspects de la construction de classe ou de vous inquiéter de la métaclasse conflits en bas de la ligne. Comme un message par Nick Coghlan sur la première phase de ce PEP états:

Le principal but de la lisibilité/maintenabilité avantage est de la point de vue de plus en distinguant clairement la "sous-classe personnalise initialisation" l'affaire de la "personnalise runtime comportement de les sous-classes" de cas.

Une mesure métaclasse ne fournit pas une indication de la portée de impact, tandis que l' __init_subclass__ plus indique clairement qu'il n'y a pas les effets persistants sur le comportement post-sous-classe de la création.

Metaclasses sont considérées comme de la magie pour une raison quelconque, vous ne savez pas quels sont leurs effets après la classe sera créée. __init_subclass__, d'autre part, est juste une autre méthode de la classe, elle s'exécute une fois et puis c'est fait. (consultez sa documentation pour exactement la fonctionnalité.)


Le point de l'ensemble de PEP 487 est sur la simplification (j'.e la suppression de la nécessité d'utiliser) metaclasses pour les utilisations courantes.

__init_subclass__ prend soin de post-initialisation de classe tout en __set_name__ (qui n'a de sens que pour le descripteur de classes) a été ajouté afin de simplifier l'initialisation de descripteurs. Au-delà, ils ne sont pas liés.

Le troisième cas le plus courant pour les metaclasses (en gardant la définition de l'ordre) qui est mentionné, a également été simplifiée. Cela a été adressée w/o d'un crochet, en utilisant un ensemble ordonné pour la cartographie de l'espace de noms (qui en Python 3.6 est un dict, mais c'est un détail d'implémentation :-)

8voto

pylang Points 12013

Je voudrais ajouter quelques références liées à metaclasses et __init_subclass__ qui peuvent être utiles.

Arrière-plan

__init_subclass__ a été présenté comme une alternative à la création d'metaclasses. Ici se trouve à 2 minutes de résumé de PEP 487 en parler par l'un des principaux développeurs, Brett Canon.

Références Recommandées

  • Guido van Rossum du blog sur l'histoire des débuts de metaclasses en Python
  • Jake Vanderplas du blog à la recherche de plus en plus profondément sur la mise en œuvre de metaclasses

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X