Maison >développement back-end >Tutoriel Python >Explication détaillée du rendement de la syntaxe dans Python 3
Dans python 3.3, le générateur a une nouvelle syntaxe rendement from. Quelle est la fonction de rendement from ? Quelle est la syntaxe ? L'article suivant vous présente principalement en détail les informations pertinentes sur la syntaxe du rendement dans Python 3. Les amis qui en ont besoin peuvent s'y référer.
Préface
Je bricolais Autobahn récemment, et il a donné un exemple basé sur asyncio. J'ai pensé à le mettre sur pypy3 et à l'exécuter, mais c'est... . échoué. . pip install asyncio
J'ai signalé directement une syntaxe invalide. À première vue, je pensais qu'il y avait un problème avec le traitement 2to3 - vous ne pouvez pas me blâmer, de nombreux packages ont été écrits en 2 puis convertis en 3 - il s'est avéré que c'était uniquement asyncio. prend en charge la version 3.3., puis j'ai regardé le code, j'ai soudainement trouvé une phrase yield from
je sais, mais yield
est un cheval magique ? yield from
PEP-380
Eh bien, j'ai trouvé ce titre via Google. La vie passée et présente de est toute dans ce PEP. l'idée générale est celle d'origineyield from
L'instruction ne peut rendre le contrôle du CPU qu'à l'appelant direct. Lorsque vous souhaitez reconstruire la logique avec une instruction rendement dans un générateur ou une coroutine dans un autre générateur (sous-générateur d'origine), ce sera très. gênant, car à l'extérieur Le générateur est responsable du passage des messages pour le générateur à l'intérieur ; donc quelqu'un a eu l'idée de laisser Python encapsuler le message qui passe pour le rendre transparent pour les programmeurs, donc il y avait yield
. yield from
, ou le modèle de comportement que devraient avoir les générateurs imbriqués. yield from
yield from B()
renvoie un objet itérable (itérable) b , alors A () renverra un générateur - selon notre convention de dénomination, le nom est a - puis : B()
de b est appelée, sinon la méthode d'envoi. de b est appelé . Si l'appel de méthode à b génère une exception StopIteration, a continuera à exécuter les instructions qui suivent __next__()
, et d'autres exceptions seront propagées à a, obligeant a à lever une exception lors de l'exécution de yield from
. yield from
dans a est le premier paramètre de l'exception StopIteration levée à la fin de l'itération de b. yield from
dans b lancera en fait une exception return 942bcdedd51e2f7d833cc5298f53fcca
, donc la valeur de return dans b deviendra la valeur de retour de l'expression StopIteration(942bcdedd51e2f7d833cc5298f53fcca)
dans a. yield from
Un exemple inutile
C'est inutile car vous n'avez probablement pas vraiment envie d'écrire le programme comme ça, mais... Quoi qu'il en soit, c'est suffisant pour illustrer le problème . Imaginez une fonction génératrice comme celle-ci :def inner(): coef = 1 total = 0 while True: try: input_val = yield total total = total + coef * input_val except SwitchSign: coef = -(coef) except BreakOut: return totalLe générateur généré par cette fonction accumulera la valeur reçue de la méthode d'envoi pour le total de la variable locale et arrête l'itération lorsqu'une exception BreakOut est reçue ; comme pour l'autre exception SwitchSign, elle ne devrait pas être difficile à comprendre, donc je ne la gâcherai pas ici. Du point de vue du code, le générateur obtenu par la fonction
reçoit les données à utiliser via l'envoi, et accepte en même temps le contrôle du code externe via la méthode throw pour exécuter différentes branches de code. est très clair jusqu'à présent. inner()
. Puisque je pense "ne touchez pas au code qui n'est pas cassé", j'ai décidé de laisser inner()
tel quel, puis d'en écrire un autre inner()
, de mettre le code ajouté à l'intérieur de outer()
et de fournir les mêmes opérations que outer()
interfaces. Puisque inner()
utilise plusieurs fonctionnalités du générateur, inner()
doit également faire ces cinq choses : outer()
outer()
必须生成一个generator;
在每一步的迭代中,outer()
要帮助inner()
返回迭代值;
在每一步的迭代中,outer()
要帮助inner()
接收外部发送的数据;
在每一步的迭代中,outer()
要处理inner()
接收和抛出所有异常;
在outer()
被close的时候,inner()
也要被正确地close掉。
根据上面的要求,在只有yield的世界里,outer()
可能是长这样的:
def outer1(): print("Before inner(), I do this.") i_gen = inner() input_val = None ret_val = i_gen.send(input_val) while True: try: input_val = yield ret_val ret_val = i_gen.send(input_val) except StopIteration: break except Exception as err: try: ret_val = i_gen.throw(err) except StopIteration: break print("After inner(), I do that.")
WTF,这段代码比inner()
本身还要长,而且还没处理close操作。
现在我们来试试外星科技:
def outer2(): print("Before inner(), I do this.") yield from inner() print("After inner(), I do that.")
除了完全符合上面的要求外,这四行代码打印出来的时候还能省点纸。
我们可以在outer1()
和outer2()
上分别测试 数据 以及 异常 的传递,不难发现这两个generator的行为基本上是一致的。既然如此, 外星科技当然在大多数情况下是首选。
对generator和coroutine的疑问
从以前接触到Python下的coroutine就觉得它怪怪的,我能看清它们的 行为模式,但是并不明白为什么要使用这种模式,generator和 coroutine具有一样的对外接口,是generator造就了coroutine呢,还 是coroutine造就了generator?最让我百思不得其解的是,Python下 的coroutine将“消息传递”和“调度”这两种操作绑在一个yield 上——即便有了yield from
,这个状况还是没变过——我看不出这样做 的必要性。如果一开始就从语法层面将这两种语义分开,并且为 generator和coroutine分别设计一套接口,coroutine的概念大概也会 容易理解一些。
更多Python 3中的yield from语法详解相关文章请关注PHP中文网!