8
votes

Invoquant `Super` dans la méthode de classe appelée de la métaclasse .__ NOUVEAU__

J'ai un cas où ma classe a une métaclasse personnalisée, qui appelle une méthode de classe de la classe lorsqu'elle la crée, quelque chose comme: xxx

(exemple de ce code est dans TASTYPIE .)

Le problème est si je veux faire: xxx

Ceci ne fonctionne pas car newbar n'est pas encore créé au point Super est invoqué (le flux de programme est toujours en métaclass). Alors, y a-t-il une solution de contournement?

Je sais que probablement get_fields pourrait devenir une méthode de métaclasse, mais cela rendrait l'héritage beaucoup plus difficile à mettre en œuvre (vous devriez définir les deux Nouvelle métaclasse et classe elle-même, pas agréable aux développeurs souhaitant prolonger ces cours).

(Python 2.7.)


0 commentaires

3 Réponses :


2
votes

si newbar code> peut être indisponible lorsque get_fields code> est invoqué, vous pouvez toujours le trouver dans le MRO de CLS code>:

@classmethod
def get_fields(cls):
    # we can get invoked before NewBar is available in globals,
    # so get NewBar from cls.__mro__
    NewBar = next(c for c in cls.__mro__
                  if c.__module__ == __name__ and c.__name__ == 'NewBar')
    super(NewBar, cls).get_fields()
    ...


8 commentaires

J'ai fini par utiliser: moi = Suivant (c pour C en CLS .__ MRO__ IF Si c .__ Module__ == __Name__ et c .__ Nom__ == 'Newbar')


J'aime le c .__ module__ == __Name __ Amélioration! Je vais mettre à jour la réponse.


Qu'en est-il de essayer: Super_class = Newbar; SaufameError: Super_class = CLS; Super (super_class, CLS) .get_fields () ?


@kropoolik Ça marche, mais il est obscurcié d'une manière différente. Le code dans la réponse indique clairement que nous essayons simplement de trouver newbar afin d'appeler super de la manière la plus régulière possible. Dans votre variante, l'invocation Super apparaît encore plus magique que nécessaire.


Une troisième variante telle que essayer: newbar; SaufameError: Newbar = CLS; ... conserverait le truc NameError , mais ce qui précise que nous recherchons vraiment newbar . Cela me rendrait toujours maigre que ce n'est pas Assurez-vous , il a réellement trouvé newbar . Peut-être un "affirmer CLS .__ nom__ ==" Newbar`` serait en ordre là-bas.


En effet, nous ne pouvons pas être certains que CLS dans sauf: est newbar , mais nous avons 2 cas: appel de notre métaclass ( Newbar. __metaclass __ ) - Ici Newbar n'a pas fini de construction n'est donc pas disponible en tant que nom global et CLS est le NEWBAR objet. Deuxièmement - NEWBARDERIVER .__ METACLASS __ , nous construisons ici une classe qui dérive à partir de NEWBAR , donc newbar est un identifiant valide, à moins que quelqu'un crée une nouvelle classe interne Newbar. __metaclass __ etc. Mais c'est la sorcellerie, pas la programmation. Le affirmer est bien comme pour notifie le codeur que quelque chose est vêlé.


La variante suivante () n'est pas garantie de trouver la classe souhaitée, d'être honnête (penser à newbar.get_fields.im_func (, champs, exclut) ) , mais pourrait causer un comportement inattendu tandis que Essayez: Sauf: assert est auto-expliquant.


@KROOOLIK Les exemples pathologiques d'appels vers get_fields ne sont heureusement pas sur la table, car nous savons exactement comment get_fields est appelé. ( Le METACLASS` est en réalité définie dans le cadre de la saveur de goût.) La réponse doit choisir le moins choisir, comme vous le mettez, une solution sorceuse, et c'est parfois une question de goût. Vous pouvez simplement soumettre votre code comme réponse alternative.



0
votes

basé sur la réponse de @ user4815162342 J'ai trouvé une solution encore plus simple: xxx


7 commentaires

Plus simple au détriment de l'exactitude, malheureusement. En particulier, 'newbar' dans str (e) ressemble à un bogue en attente de se produire, d'autant plus que ce type de code doit être dans chaque implémentation remplacée de get_fields . Par souci de maintenance future de ce code, je recommanderais sérieusement une approche plus propre.


Hm, oui, mais je ne peux pas changer d'original get_fields méthode. Il vient de la bibliothèque externe.


Je vois. Ce n'était pas clair à partir de votre exemple car foobar , le premier à définir get_fields , utilise déjà votre métaclasse, il était donc raisonnable de supposer que foobar , et l'ensemble de l'interface get_fields , vous a été défini par vous.


Alors, est la métaclasse sous votre contrôle? Si tel est le cas, je pense que je peux modifier ma réponse au travail sans changer la signature de get_fields .


Peut-être que je devrais fournir un lien direct vers le code dans TASTYPIE < / a>.


Ou ... vous pouvez remplacer l'essai / à l'exception de la logique avec moi = newbar si "newbar" dans les globaux () sinon CLS puis appelle super avec super ( Moi, CLS) .get_fields () . Toujours un peu piraté, mais plus simple que ma proposition, et raisonnablement facile à suivre.


J'ai mis à jour ma réponse pour utiliser une troisième solution, qui doit simplement rechercher newbar dans CLS .__ MRO __ . Celui-ci semble le moins moche - pas de bruit avec global sous quelque forme que ce soit - mais il nécessite toujours une recherche à base de chaînes via MRO.



0
votes

Je sais que la question est spécifique à Python 2.7, cependant, pour ceux qui utilisent Python 3.6, vous pouvez simplement appeler Super () code>.

class NewBar(FooBar):
    @classmethod
    def get_fields(cls):
        super().get_fields()
        ...


0 commentaires