10
votes

Invoquant le rendement pour un générateur dans une autre fonction

Supposons que j'ai un objet manager. L'API de cet objet a une fonction MAIN_HOOK , qui obtient une autre fonction f comme son argument et exécute le f dans une boucle, fais des trucs. entre chaque itération: xxx

maintenant, j'ai aussi (plus précisément, aimerait avoir ) une fonction stop_and_do_stuff , une fois Appelé, arrête MAIN_HOOK morts dans ses pistes, renvoie le contrôle à la fonction de fonctionnement Main_Hook , et après que Func a terminé ce qu'il fait, obtenez le contrôle à Main_Hook et continuez. Fondamentalement, le résultat sera le même que de faire xxx

sauf que rendement je veux avoir un appel à f () , tout en donnant f l'option d'appeler self.stop_and_do_stuff ()

i ne peut pas travail autour de cela En faisant aussi un générateur pour 2 raisons:

1. f ne fait pas partie de mon API - cela me est donné par un utilisateur qui utilise ma lib

2.Even Si cela pourrait lui demander d'utiliser le rendement, la place dans le code dans lequel il devra appeler stop_and_do_stuff ne sera pas directement à l'intérieur de F, plutôt à un endroit dans la pile de fonctions qui sera à l'intérieur f () , mais pas directement dedans, par exemple xxx

donc si je choisis de faire f un générateur, j'ai aussi besoin de faire g un générateur et aussi h , sinon ce truc ne fonctionnera pas.

y a-t-il une solution à tout ça? Peut-être que j'essaie de résoudre le mauvais moyen?

(Je sais que cette question est longue et moche - c'est le meilleur que je puisse faire. Si quelque chose n'est pas clair, dites-moi et je 'll clarifiez-le)

edit

peut-être PEP 342 est la solution?


3 commentaires

J'ai la même compréhension que ANurag et également penser (comme lui) que vous n'avez vraiment pas posé de question mais que vous avez vraiment posé des éléments de votre propre solution (cela ne fonctionne pas encore). Donc, le mieux que vous puissiez attendre est de getter votre solution pour travailler, ne pas avoir une solution vraiment pythonique. De plus, de ce que j'ai vu dans la question, j'ai un sentiment étrange. Il me semble étrange de parler de fonctions comme "faire quelque chose" au lieu de "retourner un résultat", sonne comme ce que vous faites est surtout un effet secondaire interactif. Est-ce?


Il n'est pas clair pour moi que si F est une fonction de libération étrangère comment peut-il appeler stop_and_do_stuff au milieu et s'il peut faire cela pourquoi ne peut-il pas céder?


@ Anurag-f obtenir un objet comme son argument, et il a la fonction stop_and_do_stuff


5 Réponses :


0
votes

Je ne suis pas tout à fait sûr de quoi vous essayez exactement d'atteindre, alors peut-être que si vous pouvez expliquer le problème plus au lieu de donner une solution qui serait meilleure.

de ma compréhension partielle Pourquoi ne faites-vous pas quelque chose comme ça xxx

si fondamentalement f renvoie un drapeau à arrêter ou non, et s'il dit que nous cédons, nous cédons à la fonction qui appelait Main_Hook et cette fonction peut continuer. Après avoir fait des choses

par exemple xxx


3 commentaires

Cette solution est loin du résultat souhaité. Tout le point était que f peut appeler stop_and_do_stuff au milieu de celui-ci, sans finir (c'est-à-dire comme si f a un rapport < / code> dedans). Dans votre solution, f doit finir et renvoyer un drapeau, qui force f pour terminer avant stop_and_do_stuff peut être appelé. En ce qui concerne ma question, si vous pouvez élaborer ce qui n'est pas clair dans mon explication, je serais heureux de le refuser.


@NOAM: une partie de ce qui est difficile est le "stop_and_" dans "stop_and_do_stuff". Soit vous voulez arrêter f () comme un retour, c'est ce que l'Anurag comprit, soit c'est juste une fonction habituelle à l'intérieur de F () (en ce qui est également facile, il suffit de fournir une fonction à appeler). On dirait que vous voulez appeler une fonction, mais pas le fournir explicitement. L'utilisation habituelle du rendement en Python est l'autre sens (redonner le contrôle), votre utilisation est plus un rendement de Ruby (appel à la fonction anonyme). Le problème plus large est que vous n'expliquez pas ce que vous essayez d'atteindre, mais seulement comment vous l'avez fait.


C'est la meilleure solution jusqu'à présent, mais cela change main_hook qui n'est pas aussi demandé



1
votes

Je ne comprends pas le tout (qu'on ressemble l'appelant principal_hook?), mais je dirais, jetez une exception STOPNOW, lorsque vous devez vous arrêter, tout comme vous devriez jeter l'arrêt lorsque votre générateur est terminé. < P> Voici comment j'ai compris la chose aussi bien que ce que je ferais. xxx


1 commentaires

La question n'était pas "Comment puis-je abandonner la fonction?", Mais plutôt "Comment je la suspendre de manière à ce que je puisse reprendre la suite?" Un générateur fait exactement cela, en utilisant le rendement. Le seul problème est que si le générateur souhaite appeler une fonction qui devrait céder, cette fonction devient plutôt un générateur lui-même, qui ruine tout. La question est de savoir comment se déplacer. J'ai posté ce que je fais actuellement comme une réponse, mais je suis certainement intéressé par des solutions plus élégantes.



0
votes

Le comportement que vous décrivez semble exactement comme un appel de fonction simple. Comme ci-dessous.

def f(manager):
    print("Entering f")
    manager.stop_and_do_stuff()
    print("Exiting f")

class Manager(Object):
    def shouldContinue(self):
        return True

    def stop_and_do_stuff(self):
        print("Manager stop and do stuff")

    def main_hook(self,f):
        while self.shouldContinue()
            print("Manager Setup")
            f(self)
            print("Manager Tear Down")


2 commentaires

Comme je le comprends (et au moins, ce que j'ai un problème avec moi-même), c'est qu'il veut écrire un générateur complexe, qui ne donnera pas (seulement) donner "directement", mais appelle également des fonctions qui l'aboutiront. Cependant, lors de la mise en place d'une fonction dans une fonction, cette fonction devient un générateur lui-même, de sorte que cela ne fonctionne pas. Ma propre réponse ici montre ma solide laid de contournement. Si vous connaissez un moyen plus élégant de le faire, s'il vous plaît laissez-nous savoir. Pep 342 parle de Coroutines, qui sont exactement ce que nous voulons. Et nous voulons appeler des fonctions de Coroutines. Cela ne semble pas possible.


Main_Hook S doit démarrer et arrêter à la volonté de l'utilisateur extérieur, comme un générateur.



4
votes

Je crois que je devrais également ajouter une réponse de l'autre point de vue, c'est-à-dire ne pas essayer d'expliquer comment vous pourriez obtenir ce que nous pouvons comprendre de ce que vous essayez de faire, mais pourquoi rendement code> définitivement ne pouvait peut-être pas fonctionner.

Lorsqu'une fonction contient rendement code> mot clé, il est profondément modifié. C'est toujours un appelable mais pas une fonction normale plus: il devient une usine qui renvoie un itérateur. P>

du point de vue de l'appelant, il n'y a aucune différence entre les trois implémentations ci-dessous (sauf que le Rendement code> est tellement plus simple). P>

def main_hook(self,f):
    while (self.shouldContinue()):
        #do some preparations
        for v in f(self):
            yield v
        #do some tear down


0 commentaires

4
votes

Ma réponse précédente décrit comment faire cela à Python2, qui est très laid. Mais maintenant, j'ai couru sur PEP 380 : Syntaxe de délégation d'un sous-générateur. Cela fait exactement ce que vous demandez. Le seul problème est que cela nécessite Python3. Mais cela ne devrait pas vraiment être un problème.

Voici comment ça marche: P>

generator yields 0
generator yields 1
generator yields 2
returned 3
generator yields 4


7 commentaires

Cela ne le résout pas ... vous avez fait principal utilise le rendement directement maintenant, contrairement aux états de la question.


Ces rendements sont juste pour montrer l'ordre dans lequel les choses sont exécutées. "Rendement 0" et "rendement 4" peuvent être supprimés. La commande "rendement de" fonctionnera toujours (et signifiera également que le principal est une fonction de générateur elle-même).


Exactement. Le principal ne peut pas devenir un générateur.


Cela peut, et ça doit. Il n'y a pas d'autre moyen de faire cela. Comme OP écrit à la fin, F et G et H doivent tous être des générateurs. C'était très encombrant à Python2, mais avec "rendement de" c'est très élégant. Bien sûr, il existe une option pour ne pas créer F A Generator, mais dans ce cas, il ne peut pas attendre un autre générateur (ni lui-même ni aucune des fonctions qu'elle appelle). Si tel est ce que veut que l'utilisateur veut, la bibliothèque peut appeler F, voir si le résultat est un générateur et, le cas échéant, itérale dessus. Sinon, utilisez (ou jeter) la valeur de retour.


La question indique qu'il ne peut pas, alors la réponse est invalide si elle doit.


N'hésitez pas à ajouter une réponse qui indique simplement "c'est impossible". C'est vrai, mais je crois que cette réponse est plus utile.


La réponse acceptée stipule que.