J'ai une classe qui doit faire une magie avec chaque opérateur, comme au lieu de créer chaque fonction dans la classe , J'ai une métaclasse qui définit chaque opérateur dans le module de l'opérateur. P> __ ajouter __ code>,
__ sous __ code> et ainsi de suite.
>>> f = FuncBuilder()
>>> g = f ** 2
>>> g(10)
100
>>> g
<var [('pow', 2)]>
4 Réponses :
On dirait que vous faites des choses trop compliquées. Vous pouvez définir une classe de mixine et hériter de celui-ci. Ceci est à la fois plus simple que d'utiliser des métaclasses et fonctionnera plus rapidement que d'utiliser alors chaque classe que vous souhaitez avoir ces opérateurs, hériter de l'operatortimixin. P > générant les méthodes de l'opérateur lorsqu'il est nécessaire n'est pas une bonne idée: __ getattr __ code>.
__ getattr __ code> est lent par rapport à la recherche de méthode ordinaire, et puisque les méthodes sont stockées. Une fois (sur la classe Mixin), il ne fait presque rien. P> p>
Oui, il y a au moins 10 opérateurs (plus des formulaires surveillé) et je ne veux pas les écrire à la main et appeler exactement la même fonction (changer l'opérateur) pour chacun d'eux.
Mon idée est maintenant de seulement i> créer le Func code> ou
rfuncon code> lorsque l'opérateur est appelé.
Qu'est-ce qui créera les fonctions qui vous donneront paresseusement?
C'est parce que la classe est un générateur de fonction. Il est conçu pour être utilisé peu de fois et l'objet résultant est celui qui est appelé autant de fois que l'utilisateur veut.
Dans ce cas, je ferais une classe pleine de fonctions telles que "Make_adder", etc. N'abandonnez rien. Ensuite, sous-classe de cette baseClass pour chaque classe spécifique dont vous avez besoin.
Cela enlèverait le plaisir d'utiliser des opérateurs;)
__ getattr __ code> n'est même pas appelé pour
__ xxx __ code> méthodes. Donc, oui, performance moche. ;)
Quelques magies noires Vous permet de réaliser votre objectif: sortie strong> p> maintenant que vous pouvez utiliser Votre deuxième échantillon de code littéralement en héritant de mon classe de base code> classe de base. Vous obtenez même un avantage supplémentaire:
__ getattr __ code> sera appelé une seule fois par instance et opérateur et il n'y a pas de couche supplémentaire de récursivité impliquée pour la mise en cache. Nous contourgons par la présente le problème des appels de méthode lentement par rapport à la recherche de méthode (comme Paul Hankin remarqua correctement). P>
Eh bien, on dirait que votre pour code> La boucle ajoute tous les opérateurs à la classe (regardez mon code, je le fais aussi). Je veux pas b> les avoir :). BTW, je pense que c'est déjà une amélioration.
@Jbernardo: regarde à nouveau. Cela fonctionne entièrement différent de votre code. Ce qui est ajouté ne sont pas les fonctions de l'opérateur créées, mais uniquement des emballages peu profonds autour d'un __ getattr __ code> appel. Cela est nécessaire, car, comme vous l'avez dit, vous ne pouvez pas intercepter ces appels de méthode à l'aide d'une fonction personnalisée
__ getattr __ code>. Comme la boucle n'est exécutée que une fois dans votre programme entier i> et que le nombre d'opérateurs est fini, il prend des frais généraux constants dans la gamme de millisecondes. Fondamentalement, il s'agit d'un hack pour vous permettre d'utiliser
__ getattr __ code> pour intercepter les opérateurs tels que toutes les autres méthodes (ce que vous avez demandé exactement).
Je comprends votre code (vous devez également ajouter ces commentaires à la réponse), mais ce que vous faites est: x + y -> x .__ add__ -> x .__ getattr __ ('__ ajouter __') code>. C'est une idée intéressante, mais semble ne pas avoir que les opérateurs sont impossibles d'une manière ou d'une autre.
C'est plus comme x + y -> x .__ add__ -> cached_func = x .__ getattr __ ('__ ajouter __') code>. La deuxième fois que c'est
x + y -> cached_func code> directement. Vous avez raison dans la mesure où il n'est pas possible d'intercepter l'addition sans avoir un
__ ajouter __ code> méthode (pourquoi devrait-il être?). Cela devrait être le plus proche que vous puissiez accéder à une solution de votre problème.
J'accepte votre réponse car est le plus proche que je puisse obtenir de ce que je veux, mais j'attendrai un peu plus pour donner la prime. Merci
Ok, assez juste. Peut-être que quelqu'un propose une solution plus élégante :)
@Éthanfurman: Je suis un codeur rubis, alors je dois aimer les métaclasses :)
Tout comme un point de clarification, vous n'utilisez pas réellement de métaclasses au sens Python.
@Éthanfurman: Je sais, c'est plus une simulation de singleton / d'Eigen / Métaclasses à Ruby.
@ Niklasb.justez une petite question - pourquoi créeriez-vous la classe temporaire d'instance? Cela fonctionnera parfaitement sans cela et compte tenu du fait que ses méthodes vont utiliser des variables d'instance et non des variables pré-définies, alors elles fonctionnent parfaitement dans n'importe quel autre exemple de la même classe.
Le problème à la main est que Python recherche Le seul moyen d'intercepter de tels appels est d'avoir une méthode déjà là. Il peut s'agir d'une fonction de talon, comme dans la réponse de Niklas Baumstark, ou il peut s'agir de la fonction de remplacement à part entière; de toute façon, cependant, là doit em> être quelque chose déjà là ou que vous ne pourrez pas intercepter de tels appels. P>
Si vous lisez de près, vous aurez remarqué que votre exigence pour que la méthode finale soit liée à l'instance n'est pas une solution possible - vous pouvez le faire, mais Python ne l'appellera jamais comme Python regarde le Classe de l'instance, pas l'instance, pour __ xxx __ code> sur la classe de l'objet, non sur l'objet lui-même - et s'il n'est pas trouvé, il ne retombe pas à
__ getattr__ code> ni
__ getattribute __ code>. p>
__ xxx __ code> méthodes. La solution de Niklas Baumstark de la fabrication d'une classe Temp unique pour chaque instance est aussi proche que possible de l'exigence. P>
Si vous souhaitez atteindre votre objectif sans métaclasses, vous pouvez ajouter ce qui suit à votre code:
>>> l = ShowMeList(range(8)) >>> len(l) Wrapping 8 >>> l Wrapping [0, 1, 2, 3, 4, 5, 6, 7] >>> print(l) Wrapping [0, 1, 2, 3, 4, 5, 6, 7]
"J'ai une classe qui doit faire de la magie avec chaque opérateur" - pourquoi? On dirait que vous aboyer un arbre très compliqué ...
@Lennartregebro J'écris un générateur de fonctions utilisant les opérateurs sur certains objets.
F = FuncBuilder (); g = f ** 2 + 1; g (10) == 101 code>. Ce n'est pas quelque chose de très utile (beaucoup d'appels de fonction), mais est un peu amusant à utiliser: D
@Lennartregebro J'ai posté le code complet.
Ok, vous avez donc créé un moyen de faire de Lambdas. :-) Tant que vous le faites pour le plaisir, tout va bien. :-)