12
votes

Comment faire des œuvres super () en remplissant manuellement la cellule __Class__?

en Python 3 on peut utiliser super () code> au lieu de super (myClass, auto) code>, mais cela ne fonctionne que dans des méthodes définies à l'intérieur de la classe. Comme décrit dans Article de Michele Simionato L'exemple suivant ne fonctionne pas:

__init__.__class_cell_thingy__ = C


15 commentaires

"Est-il possible de définir cette cellule manuellement"? Quand tu as essayé ça, que s'est-il passé?


Mais comment puis-je avoir accès à cette cellule après avoir défini la fonction? __ init __.__ classe __ est bien sûr autre chose. J'ai maintenant édité la question pour clarifier cela.


Pourquoi voulez-vous définir la fonction en dehors de la classe? Vous voudriez cela si vous souhaitez réutiliser une fonction ou utiliser une fonction comme méthode plusieurs fois. Vous ne pouvez pas faire ces choses et utiliser super (...) correctement. Et si vous modifiez une classe au moment de l'exécution, vous pouvez simplement le calculer.


@RUSH: Évidemment, les cas d'utilisation pour cela sont un peu compliqués. Dans mon cas, il est lié à la programmation orientée forme. La fonction n'est ajoutée qu'à une classe, donc je pouvais le creuser (mais ce n'est pas ma question). Si vous pensez que ce que je demande est impossible, veuillez expliquer pourquoi, et je serai heureux d'accepter votre réponse.


@nikow: Si vous aimez la programmation orientée de l'aspect, jetez un coup d'œil à l'architecture du composant Zope, c'est-à-dire.


Si vous voulez faire un AOP, pourquoi ne pas utiliser de décorateurs?


@Lennart: Merci pour la référence. Je pense que l'AOP est utilisé dans des projets de python assez nombreux. Malheureusement, je n'ai rien trouvé directement lié à ma question.


@ S.Lott: Dans le code actuel, j'utilise des décorateurs de fonction. Mais cela ne m'aide pas avec les appels super à l'intérieur de la fonction ou?


@nikow: Eh bien, il semble une meilleure idée d'appeler super () à l'intérieur du propre __ init __ , puis peut-être peut-être envelopper cet init avec un décorateur.


@nikow: plutôt que d'un AOP hypothétique comme celui-ci, il est probablement préférable d'ouvrir une nouvelle question avec l'aspect spécifique qui semble faire partie de __ init __ . Souvent, cela ne fait pas partie de __ init __ . Il est plus facile de travailler avec des cas d'utilisation spécifiques et tangibles.


@ S.Lott: Peu importe que j'ai utilisé la méthode __ init __ dans mon exemple (que j'ai pris à partir de l'article référencé). Il me semble que ma question était assez spécifique et j'ai eu une excellente réponse. Donc, je ne comprends pas vraiment pourquoi je devais tellement défendre cette question.


@nikow: "Je ne comprends pas vraiment pourquoi je devais tellement défendre cette question". Réfléchissons. Choix 1: Nous vous haïssons tous. Choix 2: La question pourrait être une amélioration. Choix 3: Nous sommes tous des idiots totaux. Choisir un.


@ S.LOTT: Vous êtes invité à modifier la question ou à faire des suggestions sur la manière de l'améliorer. Malheureusement, je ne comprends toujours pas votre commentaire précédent sur le __ init __ init __


@nikow: «Je ne comprends toujours pas votre commentaire précédent sur le init " Je m'excuse. C'était une demande pour une nouvelle question avec un étui concret, spécifique, tangible et détaillé pour la raison pour laquelle vous voudrez même faire cela.


@ S.Lott: Ah, je vois. Mon cas d'utilisation est un peu complexe, car il ne s'agirait que d'un peu de fonctionnalités pour un mécanisme d'extension de style AOP déjà existant dans un projet plus vaste. Je voulais juste déterminer s'il serait possible d'ajouter une prise en charge automatique pour Super () une fois que la transition de Python 3 est terminée. En raison de la nature et de la complexité de la solution suggérée, je ne pense pas que cela en vaut la peine.


3 Réponses :


1
votes

Peut-être, mais par voudriez-vous? Dans les deux cas, vous devez être explicite de quelle classe c'est, car la manière implicite n'a pas fonctionné. Peut-être que vous pouvez définir la cellule explicitement en quelque sorte, mais il n'y a aucune raison de le faire. Il suffit de passer les paramètres explicitement.

def __init__(self):
    print('calling __init__')
    super(C, self).__init__()

class C(object):
    __init__ = __init__

if __name__ == '__main__':
    c = C()


8 commentaires

Merci d'avoir répondu, mais ce que vous suggérez ici est très faux. Vous ne pouvez pas utiliser auto .__ classe __ avec Super , comme cela a été récemment exclaté ici: Stackoverflow.com/Questtions/4883822/...


@nikow: Eh bien, si vous allez sous-classer, vous pouvez avoir des problèmes, oui, si cette sous-classe utilise également Super (). Donc, vous voulez utiliser le même __ init __ pour de nombreuses classes différentes (car sinon, vous ne l'auriez pas définis en dehors de la classe) et que vous ne savez pas quelles classes, et que vous ne savez pas si Ou comment ces classes sont sous-classées? Dans ce cas, je dirais que vous résolvez le problème de problème. Puis-je suggérer un décorateur de classe ou une architecture de composants avec des adaptateurs?


@Lennart: La fonction ajoutée est définie ailleurs après des idées d'AOP, ce qui a assez bien fonctionné dans notre projet. Dans le code réel, ces "aspects" sont activés / désactivés au moment de l'exécution, il n'est donc pas vraiment une option de décorateur. J'espérais juste que l'on pourrait activer les appels simplifiés super () , après tout, ils ont été ajoutés à Python 3 pour une raison.


@nikow: Bien sûr, je comprends. C'est juste que Calling Super () des méthodes ajoutées dans votre cas semble être une mauvaise idée. D'où la suggestion d'utiliser des adaptateurs (qui répondent à la même fonction de faire définir le code ailleurs) ou des décorateurs. Depuis son exécution, les décrators sont hors de questions, mais les adaptateurs ne sont pas. Vous devriez vraiment regarder l'architecture des composants Zope, qui est orienté sur l'aspect, même si cela n'aime pas se vanter. :) Mais c'est probablement trop tard pour un projet en cours, mais vous pourriez avoir des idées.


Nono, c'est un autre impôt pour "Vous n'êtes pas censé faire cela" théorie du codage. Faire ce qui fonctionne. :)


@Lennart: J'ai examiné des cadres et des mécanismes d'extension AOP avant la mise en œuvre initiale. Bien sûr comprendre zope n'est pas exactement une tâche rapide et facile :-) Ma première mise en œuvre a réellement utilisé des adaptateurs, mais globalement plus compliqué que notre solution actuelle.


@nikow: pas zope. L'architecture du composant Zope. C'est juste une petite partie de Zope. :) Muthukadan.net/docs/zca.html


Vous devriez jamais utiliser auto .__ classe __ à super () , car maintenant si vous sousclasses c vous obtenez une infinie boucle. Soit passer dans l'objet de classe explicite, ou fournir une fermeture pour __ classe __ ; auto .__ classe __ est la mauvaise référence.



2
votes

Vous pouvez utiliser le dictionnaire de la fonction.

def f(self):
    super(f.owner_cls, self).f()
    print("B")

def add_to_class(cls, member, name=None):
    if hasattr(member, 'owner_cls'):
        raise ValueError("%r already added to class %r" % (member, member.owner_cls))
    member.owner_cls = cls
    if name is None:
        name = member.__name__
    setattr(cls, name, member)

class A:
     def f(self):
         print("A")

class B(A):
     pass

add_to_class(B, f)

B().f()


3 commentaires

Cela fonctionnerait, mais je ne suis pas sûr que je préférerais cela la solution explicite avec la classe d'écriture. Après tout, je savoir Quelle classe la méthode appartiendra, je veux juste bénéficier du plus pratique Python 3 Super () .


Si vous faites quelque chose comme celui-ci en premier lieu, vous voulez probablement pouvoir attribuer la fonction à plusieurs catégories.


@Glenn: Oui, tu as raison. Bien que ce n'était pas mon cas d'utilisation principale, je me suis souvenu qu'il y avait des cas où cela serait un problème.



26
votes

Sérieusement: vous ne voulez vraiment pas faire cela.

Mais, il est utile que les utilisateurs avancés de Python de comprendre cela, donc je vais l'expliquer. p>

cellules et féculents sont le Valeurs attribuées lorsqu'une fermeture est créée. Par exemple, p> xxx pré>

f code> renvoie une fermeture basée sur Func code>, stockant une référence à A code>. Cette référence est stockée dans une cellule (en fait dans un freevar, mais que l'on est à la mise en œuvre). Vous pouvez examiner ceci: p> xxx pré>

("cellules" et "freevars" sont très similaires. Frears a des noms, où les cellules ont des index. Ils sont tous deux stockés dans FUNC .__ Fermeture __ Code>, avec des cellules arrivant en premier. Nous nous soucions que de Freevars code> ici, car c'est ce que __ classe __ p>

Une fois que vous Comprenez cela, vous pouvez voir comment super () fonctionne réellement. Toute fonction contenant un appel à Super code> est en fait une fermeture, avec un fichier FreeVar nommé __ classe __ code> (qui est également ajouté si vous vous référez à __ class __ code> ): P>

import types

class B(object):
    def __init__(self):
        print("base")

class C(B):
    def dummy(self): __class__

def __init__(self):
    print('calling __init__')
    super().__init__()

def MakeCodeObjectWithClass(c):
    """
    Return a copy of the code object c, with __class__ added to the end
    of co_freevars.
    """
    return types.CodeType(c.co_argcount, c.co_kwonlyargcount, c.co_nlocals,
            c.co_stacksize, c.co_flags, c.co_code, c.co_consts, c.co_names,
            c.co_varnames, c.co_filename, c.co_name, c.co_firstlineno,
            c.co_lnotab, c.co_freevars + ('__class__',), c.co_cellvars)

new_code = MakeCodeObjectWithClass(__init__.__code__)
old_closure = __init__.__closure__ or ()
C.__init__ = types.FunctionType(new_code, globals(), __init__.__name__,
    __init__.__defaults__, old_closure + (C.dummy.__closure__[0],))

if __name__ == '__main__':
    c = C()


3 commentaires

Merci, excellente réponse. C'est bien de savoir comment ces choses fonctionnent. Je pense que je vais prendre votre conseil et coller à l'explicite Super appels pour le moment. Jusqu'à ce que Python 3 devienne prédominant, c'est quelque peu universitaire de toute façon.


P.s. Semble que cette réponse poussée poussée vous a poussé à 10k Rep :-)


Une autre astuce consiste à définir une fonction à l'intérieur d'une autre fonction avec un seul argument __ classe __ , puis renvoyez la fonction interne (ou juste sa fermeture). Ensuite, vous n'auriez pas besoin de la méthode factice pour créer la fermeture, ou vous pourriez également définir la méthode réelle en tant que cette fonction intérieure. Une autre chose est que vous puissiez utiliser un conteneur de classe (probablement avec une métaclasse auxiliaire avec des méthodes qui cachent la magie) pour stocker les fonctions, vous ne seriez pas à créer des objets de code personnalisés avec cet appel horaire.