Maison >développement back-end >Tutoriel Python >Quelle est la fonction du mot clé rendement en Python ?
En Python, le rôle du mot-clé rendement : 1. Modifier une fonction en générateur L'utilisation du générateur peut efficacement économiser les ressources système et éviter une utilisation inutile de la mémoire ; ; 4. Coopérer avec from pour former le rendement from, qui est utilisé pour consommer des sous-générateurs et transmettre des messages.
L'utilisation du rendement présente les quatre situations courantes suivantes :
L'une est un générateur,
Pour résumer : le code à l'intérieur du générateur sera renvoyé lorsqu'il atteint le rendement, et le contenu renvoyé est l'expression après le rendement. La prochaine fois que le code interne du générateur sera exécuté, il continuera à partir du dernier état. Grâce au mot-clé rendement, nous pouvons facilement modifier une fonction en générateur.
Le deuxième sert à définir le gestionnaire de contexte,
Le troisième est la coroutine,
Le quatrième consiste à coopérer avec from pour former un rendement from, qui est utilisé pour consommer des sous-générateurs et transmettre des messages.
Ces quatre utilisations sont en fait dérivées de la fonction de pause de rendement, c'est-à-dire que lorsque le programme s'exécute à l'emplacement où rendement est résultat = rendement expr, il exécute d'abord rendement expr. . Renvoie la valeur résultante à l'appelant qui a appelé le générateur, puis fait une pause jusqu'à ce que l'appelant redevienne actif et reprenne l'exécution du programme. En fonction de la méthode utilisée par le programme de récupération, la valeur du résultat de l'expression de rendement expr changera également en conséquence. Si elle est appelée avec __next()__, la valeur de l'expression de rendement, résultat, est None ; si elle est appelée avec send(), la valeur de l'expression de rendement, résultat, est la valeur transmise via la fonction d'envoi. Voici un exemple de l'expression de rendement introduite dans le document officiel [1], qui peut bien illustrer les caractéristiques et l'utilisation du mot-clé rendement :
>>> def echo(value=None): ... print("Begin...") ... try: ... while True: ... try: ... value = (yield value) ... except Exception as e: ... value = e ... finally: ... print("Clean up!!!") ... >>> generator = echo(1) >>> print(next(generator)) Begin... 1 >>> print(next(generator)) None >>> print(generator.send(2)) 2 >>> generator.throw(TypeError, "spam") TypeError('spam') >>> generator.close() Clean up!!!
La description du code ci-dessus est la suivante :
Lors de l'exécution du premier next(générateur), c'est-à-dire de la pré-activation du générateur, le générateur commence à s'exécuter, imprime la chaîne Begin... , et s'exécute Lorsque vous atteignez la position de valeur = (valeur de rendement), appelez d'abord la valeur de rendement pour générer le nombre 1, puis le générateur fait une pause à la position de rendement.
Lorsque le deuxième next (générateur) est appelé, le générateur reprend l'exécution. Puisque next() est utilisé pour appeler la fonction générateur, la valeur de value deviendra None, c'est donc le cas. généré Lorsque la fonction d'interpréteur continue l'exécution pour produire une valeur, la valeur de la valeur None sera renvoyée à l'interpréteur, puis mise à nouveau en pause.
Utilisez ensuite la méthode send(2) pour continuer à appeler le générateur, value reçoit le numéro entrant 2, continue d'exécuter value = (valeur de rendement) et renvoie le numéro 2 au L’interprète fait ensuite une pause.
Après cela, l'interpréteur appelle à nouveau via la méthode throw(TypeError, "spam") Le générateur reprend l'exécution et lève une exception. Le générateur intercepte l'exception et transmet l'exception TypeError. ( 'spam') est attribué à la valeur de la variable, puis le programme est à nouveau exécuté à valeur = (valeur de rendement), et TypeError('spam') est renvoyé à l'interpréteur.
Enfin, le programme appelle la méthode close(), lance GeneratorExit à l'emplacement de la fonction génératrice, l'exception est levée, le générateur se termine normalement et exécute enfin l'instruction try la plus externe correspondant à La branche finale imprime Nettoyer.
Il existe une syntaxe très utile en python appelée générateur, et le mot-clé utilisé est rendement. Une utilisation efficace de l'outil générateur peut économiser efficacement les ressources du système et éviter une utilisation inutile de la mémoire.
Générateur
Comme prévu, la première fois que vous rencontrerez le rendement sera certainement dans une fonction de générateur. Un générateur est une fonction utilisée pour générer en continu des nombres ou d'autres types de valeurs. Il peut être appelé un par un via une boucle for ou la fonction next(). Ce qu'il faut souligner ici, c'est que le générateur contient une expression de rendement sans affectation, donc les deux formes suivantes sont équivalentes [2] :
def integers_1(): for i in range(4): yield i + 1def integers_2(): for i in range(4): value = yield i + 1
La raison pour laquelle la deuxième forme est soulignée ici est que le rendement peut être meilleur compris lors de la compréhension de l’envoi de valeur via la méthode send(). Dans le même temps, on peut également expliquer plus correctement que la valeur renvoyée par l'appel du générateur est la valeur de l'expression i + 1 à droite du mot-clé rendement, et non la valeur du résultat de l'expression rendement elle-même.
Essayons d'appeler :
>>> for n in integers_1(): ... print(n) ... 1 2 3 4 >>> for n in integers_2(): ... print(n) ... 1 2 3 4
Gestionnaire de contexte
Avec le décorateur @contextmanager du module contextelib de Python, le rendement peut également être utilisé pour définir le contexte gestionnaires de contexte, voici un exemple tiré du livre Python Tricks [3] :
from contextlib import contextmanager @contextmanager def managed_file(name): try: f = open(name, 'w') yield f finally: f.close()
Le gestionnaire de contexte défini ci-dessus via le mot-clé decorator et rendement est équivalent à la définition de méthode de la classe suivante :
class ManagedFile: def __init__(self, name): self.name = name def __enter__(self): self.file = open(self.name, 'w') return self.file def __exit__(self, exc_type, exc_val, exc_tb): if self.file: self.file.close()
peut être appelé séparément en utilisant les méthodes suivantes :
>>> with ManagedFile('hello.txt') as f: ... f.write('hello, world!') ... f.write('bye now') >>> with managed_file('hello.txt') as f: ... f.write('hello, world!') ... f.write('bye now')
Coroutine
Le concept de coroutine est plein de beauté et est très cohérent avec les modèles de travail des gens. demande un certain effort pour le maîtriser pleinement. Mais l’effort en vaut la peine, car parfois le multithreading peut causer bien plus de problèmes que les coroutines. Ce qui suit est un exemple de coroutine écrite en utilisant uniquement les expressions de rendement du Python Cookbook [4] :
from collections import deque # Two simple generator functions def countdown(n): while n > 0: print('T-minus', n) yield n -= 1 print('Blastoff!') def countup(n): x = 0 while x < n: print('Counting up', x) yield x += 1 class TaskScheduler: def __init__(self): self._task_queue = deque() def new_task(self, task): ''' Admit a newly started task to the scheduler ''' self._task_queue.append(task) def run(self): ''' Run until there are no more tasks ''' while self._task_queue: task = self._task_queue.popleft() try: # Run until the next yield statement next(task) self._task_queue.append(task) except StopIteration: # Generator is no longer executing pass # Example use sched = TaskScheduler() sched.new_task(countdown(2)) sched.new_task(countup(5)) sched.run()
运行上面的脚本,可以得到以下输出:
T-minus 2 Counting up 0 T-minus 1 Counting up 1 Blastoff! Counting up 2 Counting up 3 Counting up 4
countdown 和 countup 两个任务交替执行,主程序在执行到 countdown 函数的 yield 表达式时,暂停后将被重新附加到队列里面。然后,countup 任务从队列中取了出来,并开始执行到 yield 表达式的地方后暂停,同样将暂停后的协程附加到队列里面,接着从队列里取出最左边的任务 countdown 继续执行。重复上述过程,直到队列为空。
上面的协程可以利用 Python3.7 中的 asyncio 库改写为:
import asyncio async def countdown(n): while n > 0: print('T-minus', n) await asyncio.sleep(0) n -= 1 print('Blastoff!') async def countup(n): x = 0 while x < n: print('Counting up', x) await asyncio.sleep(0) x += 1 async def main(): await asyncio.gather(countdown(2), countup(5)) asyncio.run(main())
可以看到利用 asyncio 库编写的协程示例比用 yield 来编写的协程要优雅地多,也简单地多,更容易被人理解。
yield from
说实话,yield from 实在有点令人费解,让人摸不着头脑。yield from 更多地被用于协程,而 await 关键字的引入会大大减少 yield from 的使用频率。yield from 一方面可以迭代地消耗生成器,另一方面则建立了一条双向通道,可以让调用者和子生成器便捷地通信,并自动地处理异常,接收子生成器返回的值。下面是 Python Cookbook 书里的一个例子,用于展开嵌套的序列[5]:
from collections.abc import Iterable def flatten(items, ignore_types=(str, bytes)): for x in items: if isinstance(x, Iterable) and not isinstance(x, ignore_types): yield from flatten(x) else: yield x items = [1, 2, [3, 4, [5, 6], 7], 8] # Produces 1 2 3 4 5 6 7 8 for x in flatten(items): print(x)
而 yield from 用于建立双向通道的用法则可以参考 Fluent Python 里例子[6],这里就不详细地解释这段代码:
# BEGIN YIELD_FROM_AVERAGER from collections import namedtuple Result = namedtuple('Result', 'count average') # the subgenerator def averager(): total = 0.0 count = 0 average = None while True: term = yield if term is None: break total += term count += 1 average = total/count return Result(count, average) # the delegating generator def grouper(results, key): while True: results[key] = yield from averager() # the client code, a.k.a. the caller def main(data): results = {} for key, values in data.items(): group = grouper(results, key) next(group) for value in values: group.send(value) group.send(None) report(results) # output report def report(results): for key, result in sorted(results.items()): group, unit = key.split(';') print(f'{result.count:2} {group:5} averaging {result.average:.2f}{unit}') data = { 'girls;kg': [40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5], 'girls;m': [1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43], 'boys;kg': [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3], 'boys;m': [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46], } if __name__ == '__main__': main(data)
可能对于熟练掌握 Python 的程序员来说,yield 和 yield from 相关的语法充满了美感。但对于刚入门的我来说,除了生成器语法让我感觉到了美感,其他的语法都让我理解起来很是费解。不过还好,asyncio 库融入了 Python 的标准库里,关键字 async 和 await 的引入,将会让我们更少地在编写协程时去使用 yield 和 yield from。 但不管怎么样,yield 都是 Python 里非常特别的一个关键字,值得花时间好好掌握了解。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!