7
votes

Décorateurs de classe, héritage, super () et récursion maximale

J'essaie de déterminer comment utiliser des décorateurs sur des sous-classes utilisant super () . Étant donné que mon décorateur de classe crée une autre sous-classe une classe décorée semble empêcher l'utilisation de Super () lorsqu'il modifie le nom transmis sur super (classname, auto) < / code>. Ci-dessous est un exemple: xxx

La sortie doit être: xxx

Est-ce que quelqu'un sait un moyen de ne pas casser une sous-classe qui utilise < Code> Super () Lorsque vous utilisez un décorateur? Idéalement, je voudrais réutiliser une classe de temps en temps et simplement le décorer avec la casser.


0 commentaires

5 Réponses :


2
votes

Êtes-vous sûr de vouloir utiliser un décorateur de classe et non simplement héritage? Par exemple, au lieu d'un décorateur de remplacer votre classe avec une sous-classe introduisant certaines méthodes, vous souhaitez peut-être une classe de mixine et utiliser plusieurs héritages pour créer la classe finale?

Ceci serait accompli par quelque chose comme < Pré> xxx


3 commentaires

J'ai vraiment aimé cette solution. J'utilise quelques décorateurs de classe qui prennent un paramètre (permission), cependant. Je pourrais utiliser le mélange pour changer les paramètres init , mais je pense que je préfère la flexibilité de l'ajout @Decorator (Perm) à la classe au lieu de devoir changer mon code quand je veux une classe avec le paramètre permission. Je peux utiliser cela au lieu d'autres décorateurs!


En raison de métaclasses, il est techniquement possible d'utiliser des mélanges pour n'importe quoi Vous pouvez utiliser un décorateur de classe pour. De manière générale, j'utilise des mixines si je veux ajouter des méthodes à une classe et j'utilise des décorateurs de classe lorsque je ne veux pas vraiment muter une classe du tout, mais simplement l'enregistrer ou quelque chose comme ça.


Merci, Mike! Cela ressemble à une bonne méthodologie. Comment passerais-je la permission au mixin? Est-ce que je ferais une propriété de la sous-classe lorsque je le déclare?



5
votes

Fondamentalement, vous pouvez voir le problème après avoir entré votre échantillon de code à l'invite de Python interactif: xxx pré>

c'est-à-dire, le nom sous-classe code> est maintenant lié (dans Global Portée, dans ce cas) à une classe qui, en fait, n'est pas forte> le "vrai" sous-classe code>, mais une sous-classement de celui-ci. Donc, toute référence reliée tardive à ce nom, comme celle que vous avez dans son Super (sous-classe, code> Appelez, fera bien sûr la sous-classe qui se masquait de ce nom - que la superclasse de la sous-classe est bien sûr "Le vrai sous-classe code>", d'où la récursion infinie. p>

Vous pouvez reproduire le même problème très simplement sans décoration, juste en ayant une sous-classe USURP son nom de classe de base: p> xxx pré>

maintenant, sub (). PCL () code> causera une récursion infinie, en raison de "nom d'usurpation". Décoration de classe, à moins que vous n'utilisez-le à Décorer et retourner la même classe que vous obtenez en tant qu'argument, est systématique "nom d'usurpation" systématique et incompatible avec les utilisations du nom de la classe qui doit absolument renvoyer la classe "vraie" de ce nom, et non l'usurveur (être que dans auto code> ou autrement). p>

Solutions de contournement - Si vous devez absolument avoir la décoration de classe comme usurpation (pas seulement la décoration de classe par des modifications de l'argument de classe reçu), et strong> Super code> - Besoin essentiellement des protocoles de coopération entre l'usurvateur et l'usurvateur possible, tels que les petits changements suivants à votre exemple code: P>

def class_decorator(cls):
    class _DecoratedClass(cls):
    _thesuper = cls
        def __init__(self):
            return super(_DecoratedClass, self).__init__()
    return _DecoratedClass

   ...

@class_decorator
class SubClassAgain(BaseClass):
    def print_class(self):
    cls = SubClassAgain
    if '_thesuper' in cls.__dict__:
        cls = cls._thesuper
        super(cls, self).print_class()


0 commentaires

3
votes

Comme vous le savez peut-être déjà, le problème provient du fait que le nom sous-classe code> dans sousclassagain.print_class code> est soumis à l'espace de noms global du module actuel. Sous-classe Code> fait donc référence à la classe _DecoratedClass code> plutôt que la classe qui est décorée. Une façon de se rendre à la classe décorée est de suivre une convention que les décorateurs de classe ont une propriété faisant référence à la classe décorée.

def class_decorator(cls):
    old_init = getattr(cls, '__init__')
    def __init__(self, *args, **kwargs):
        print 'decorated __init__'
        old_init(self, *args, **kwargs)
    setattr(cls, '__init__', __init__)
    return cls


0 commentaires

3
votes

Le décorateur crée une sorte de situation de héritage de diamant. Vous pouvez éviter ces problèmes en n'utilisant pas Super () . Changer sous-classe sur ce qui suit empêchera la récursion infinie: xxx


1 commentaires

Merci pour votre réponse! J'ai sélectionné cette réponse car il ne coule pas la sous-classe avec le décorateur. Je me rends compte que ma question était de savoir comment utiliser Super (), mais je suppose que la réponse est de ne pas le faire.



0
votes

Que diriez-vous simplement de promouvoir _DecoratedClass code> 'S __ bases __ code> jusqu'à la __ bases __ code> de sous-classe code>?

def class_decorator(cls):
    class _DecoratedClass(cls):
        def __init__(self):
            return super(_DecoratedClass, self).__init__()
    _DecoratedClass.__bases__=cls.__bases__
    return _DecoratedClass


0 commentaires