Maison  >  Article  >  développement back-end  >  Maîtriser les coroutines de Python : améliorez l'efficacité et les performances de votre code

Maîtriser les coroutines de Python : améliorez l'efficacité et les performances de votre code

Mary-Kate Olsen
Mary-Kate Olsenoriginal
2024-11-23 09:21:22834parcourir

Mastering Python

Explorons le monde passionnant des coroutines et de la concurrence structurée en Python. Ces fonctionnalités puissantes ont révolutionné la façon dont nous écrivons du code simultané, le rendant plus efficace et plus facile à gérer.

Les coroutines sont des fonctions spéciales qui peuvent suspendre leur exécution et céder le contrôle à d'autres coroutines. Ils sont définis à l'aide du mot-clé async et peuvent être attendus à l'aide du mot-clé wait. Voici un exemple simple :

async def greet(name):
    print(f"Hello, {name}!")
    await asyncio.sleep(1)
    print(f"Goodbye, {name}!")

async def main():
    await greet("Alice")
    await greet("Bob")

asyncio.run(main())

Dans ce code, la fonction greet est une coroutine qui imprime un message d'accueil, attend une seconde, puis dit au revoir. La fonction principale appelle greet deux fois et nous utilisons asyncio.run pour exécuter la coroutine principale.

Mais qu'est-ce qui rend les coroutines si spéciales ? Ils nous permettent d'écrire du code concurrent qui ressemble et se comporte comme du code synchrone, mais peut en réalité effectuer plusieurs opérations simultanément. Ceci est particulièrement utile pour les tâches liées aux E/S, comme les opérations réseau ou la gestion de fichiers.

Plongeons plus profondément dans la bibliothèque asyncio, qui constitue la base de la programmation asynchrone en Python. Au cœur se trouve la boucle d’événements, qui gère l’exécution des coroutines. Vous pouvez le considérer comme un planificateur qui décide quelle coroutine exécuter ensuite.

Voici comment créer et utiliser des tâches avec asyncio :

import asyncio

async def fetch_data(url):
    print(f"Fetching data from {url}")
    await asyncio.sleep(2)  # Simulating network delay
    return f"Data from {url}"

async def main():
    urls = ['http://example.com', 'http://example.org', 'http://example.net']
    tasks = [asyncio.create_task(fetch_data(url)) for url in urls]
    results = await asyncio.gather(*tasks)
    for result in results:
        print(result)

asyncio.run(main())

Dans cet exemple, nous simulons la récupération simultanée de données à partir de plusieurs URL. La fonction asyncio.create_task transforme nos coroutines en tâches, qui sont ensuite exécutées simultanément à l'aide de asyncio.gather.

Parlons maintenant de la concurrence structurée. Il s’agit d’un paradigme qui vise à rendre le code concurrent plus prévisible et plus facile à raisonner. Python 3.11 a introduit de nouvelles fonctionnalités pour prendre en charge la concurrence structurée, comme les groupes de tâches.

Voici comment utiliser un groupe de tâches :

import asyncio

async def process_item(item):
    await asyncio.sleep(1)
    return f"Processed {item}"

async def main():
    async with asyncio.TaskGroup() as tg:
        task1 = tg.create_task(process_item("A"))
        task2 = tg.create_task(process_item("B"))
        task3 = tg.create_task(process_item("C"))

    print(task1.result())
    print(task2.result())
    print(task3.result())

asyncio.run(main())

Le TaskGroup garantit que toutes les tâches sont terminées (ou annulées) avant de passer à autre chose. Cela permet d'éviter des problèmes tels que des tâches oubliées ou des interactions inattendues entre des opérations simultanées.

L'un des aspects les plus puissants des coroutines est leur capacité à gérer efficacement les opérations d'E/S. Regardons un exemple de serveur Web asynchrone simple :

import asyncio
from aiohttp import web

async def handle(request):
    name = request.match_info.get('name', "Anonymous")
    text = f"Hello, {name}!"
    return web.Response(text=text)

async def main():
    app = web.Application()
    app.add_routes([web.get('/', handle),
                    web.get('/{name}', handle)])

    runner = web.AppRunner(app)
    await runner.setup()
    site = web.TCPSite(runner, 'localhost', 8080)
    await site.start()

    print("Server started at http://localhost:8080")
    await asyncio.Event().wait()

asyncio.run(main())

Ce serveur peut gérer plusieurs connexions simultanément, grâce à la puissance des coroutines. Chaque requête est traitée dans sa propre coroutine, permettant au serveur de rester réactif même sous une charge élevée.

Explorons quelques concepts plus avancés. L'annulation est une fonctionnalité importante lorsqu'il s'agit d'opérations simultanées. Parfois, nous devons arrêter une tâche avant qu'elle ne soit terminée. Voici comment procéder :

async def greet(name):
    print(f"Hello, {name}!")
    await asyncio.sleep(1)
    print(f"Goodbye, {name}!")

async def main():
    await greet("Alice")
    await greet("Bob")

asyncio.run(main())

Dans cet exemple, nous créons une tâche de longue durée et l'annulons après 5 secondes. La tâche détecte CancelledError et effectue tout nettoyage nécessaire avant de quitter.

Une autre fonctionnalité puissante est la possibilité de créer des boucles d'événements personnalisées. Bien que la boucle d'événements par défaut soit suffisante dans la plupart des cas, nous avons parfois besoin de plus de contrôle. Voici un exemple simple de boucle d'événement personnalisée :

import asyncio

async def fetch_data(url):
    print(f"Fetching data from {url}")
    await asyncio.sleep(2)  # Simulating network delay
    return f"Data from {url}"

async def main():
    urls = ['http://example.com', 'http://example.org', 'http://example.net']
    tasks = [asyncio.create_task(fetch_data(url)) for url in urls]
    results = await asyncio.gather(*tasks)
    for result in results:
        print(result)

asyncio.run(main())

Il s'agit d'une boucle d'événements personnalisés très basique, mais elle démontre le principe. Vous pouvez étendre cela pour ajouter des fonctionnalités telles qu'une meilleure planification, surveillance ou intégration avec d'autres systèmes.

Parlons de quelques bonnes pratiques lorsque vous travaillez avec des coroutines et la concurrence structurée. Tout d’abord, utilisez toujours async with pour gérer les gestionnaires de contexte asynchrones. Cela garantit une installation et un démontage corrects, même si des exceptions se produisent :

import asyncio

async def process_item(item):
    await asyncio.sleep(1)
    return f"Processed {item}"

async def main():
    async with asyncio.TaskGroup() as tg:
        task1 = tg.create_task(process_item("A"))
        task2 = tg.create_task(process_item("B"))
        task3 = tg.create_task(process_item("C"))

    print(task1.result())
    print(task2.result())
    print(task3.result())

asyncio.run(main())

Deuxièmement, soyez prudent avec les opérations de blocage. Si vous devez effectuer une tâche liée au processeur, envisagez d'utiliser asyncio.to_thread pour l'exécuter dans un thread séparé :

import asyncio
from aiohttp import web

async def handle(request):
    name = request.match_info.get('name', "Anonymous")
    text = f"Hello, {name}!"
    return web.Response(text=text)

async def main():
    app = web.Application()
    app.add_routes([web.get('/', handle),
                    web.get('/{name}', handle)])

    runner = web.AppRunner(app)
    await runner.setup()
    site = web.TCPSite(runner, 'localhost', 8080)
    await site.start()

    print("Server started at http://localhost:8080")
    await asyncio.Event().wait()

asyncio.run(main())

Troisièmement, utilisez asyncio.wait lorsque vous avez besoin de plus de contrôle sur un groupe de tâches. Il vous permet d'attendre la fin de la première tâche, ou de définir un délai d'attente :

import asyncio

async def long_running_task():
    try:
        while True:
            print("Working...")
            await asyncio.sleep(1)
    except asyncio.CancelledError:
        print("Task was cancelled")

async def main():
    task = asyncio.create_task(long_running_task())
    await asyncio.sleep(5)
    task.cancel()
    try:
        await task
    except asyncio.CancelledError:
        print("Main: task was cancelled")

asyncio.run(main())

Le débogage du code simultané peut être difficile. L'asyncio de Python est livré avec des outils utiles. Vous pouvez activer le mode débogage pour obtenir une journalisation plus détaillée :

import asyncio

class MyEventLoop(asyncio.BaseEventLoop):
    def __init__(self):
        self._running = False
        self._ready = asyncio.Queue()

    def run_forever(self):
        self._running = True
        while self._running:
            coro = self._ready.get_nowait()
            if coro:
                coro.send(None)

    def stop(self):
        self._running = False

    def call_soon(self, callback, *args):
        self._ready.put_nowait(callback(*args))

# Usage
loop = MyEventLoop()
asyncio.set_event_loop(loop)

async def my_coroutine():
    print("Hello from my coroutine!")

loop.call_soon(my_coroutine)
loop.run_forever()

Vous pouvez également utiliser la bibliothèque aiodebug pour des fonctionnalités de débogage plus avancées.

Regardons un exemple plus complexe : un pipeline de traitement de données parallèle. Cela pourrait être utile pour des tâches telles que le traitement de grands ensembles de données ou la gestion de données en streaming :

async with aiohttp.ClientSession() as session:
    async with session.get('http://example.com') as response:
        html = await response.text()

Ce pipeline montre comment nous pouvons utiliser des files d'attente pour transmettre des données entre différentes étapes de traitement, toutes exécutées simultanément.

Les coroutines et la concurrence structurée ont ouvert de nouvelles possibilités dans la programmation Python. Ils nous permettent d'écrire du code efficace et concurrent, plus facile à raisonner et à maintenir. Que vous construisiez des serveurs Web, des pipelines de traitement de données ou des interfaces graphiques réactives, ces outils peuvent vous aider à créer des applications robustes et hautes performances.

N'oubliez pas que la clé pour maîtriser ces concepts est la pratique. Commencez par des exemples simples et passez progressivement à des cas d’utilisation plus complexes. Faites attention à la gestion et à l'annulation des erreurs, car elles sont cruciales pour créer des systèmes asynchrones fiables. Et n'ayez pas peur de vous plonger dans le code source d'asyncio : c'est un excellent moyen d'approfondir votre compréhension du fonctionnement de ces fonctionnalités puissantes sous le capot.

En continuant à explorer les coroutines et la concurrence structurée, vous découvrirez de nouveaux modèles et techniques qui peuvent rendre votre code plus efficace et plus expressif. Il s’agit d’un domaine passionnant du développement Python, en constante évolution. Alors continuez à apprendre, continuez à expérimenter et profitez du voyage dans le monde de la programmation asynchrone !


Nos créations

N'oubliez pas de consulter nos créations :

Centre des investisseurs | Vie intelligente | Époques & Échos | Mystères déroutants | Hindutva | Développeur Élite | Écoles JS


Nous sommes sur Medium

Tech Koala Insights | Epoques & Echos Monde | Support Central des Investisseurs | Mystères déroutants Medium | Sciences & Epoques Medium | Hindutva moderne

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!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn