web-dev-qa-db-fra.com

Python: gestion des exceptions Queue.Empty

Après un court débat avec quelqu'un sur la gestion des exceptions dans Python - déclenché par la gestion d'un objet de file d'attente - j'ai pensé que je le jetterais là-bas ...

MÉTHODE 1:

import Queue

q = Queue.Queue()

try:
    task=q.get(False)
    #Opt 1: Handle task here and call q.task_done()
except Queue.Empty:
    #Handle empty queue here
    pass

#Opt2: Handle task here and call q.task_done()

MÉTHODE 2:

import Queue

q = Queue.Queue()

if q.empty():
    #Handle empty queue here
else:
    task = q.get()
    #Handle task here
    q.task_done()

Un argument est que la méthode 1 est incorrecte car la file d'attente étant vide n'est pas une erreur et ne doit donc pas être gérée à l'aide de l'exception Queue.Empty. De plus, cela pourrait rendre le débogage plus difficile lorsqu'il est codé de cette façon si vous considérez que la partie de gestion des tâches peut être potentiellement volumineuse.

L'autre argument est que l'une ou l'autre façon est acceptable dans Python et que la gestion de la tâche en dehors de try/except pourrait aider au débogage si la gestion des tâches est volumineuse, bien que convenu que cela puisse paraître plus laid que d'utiliser la méthode 2.

Des avis?

MISE À JOUR: Un peu plus d'informations après la réponse 1 est arrivée .... Le débat a commencé après que la méthode 1 a été utilisée dans du code multithread. Dans ce cas, le code acquiert le verrou (à partir d'un objet threading.Lock) et le relâche soit une fois la tâche renvoyée, soit Queue.Empty est levée

MISE À JOUR 2: Nous ne savions pas tous les deux que l'objet file d'attente était thread-safe. On dirait que try/except est la voie à suivre!

18
user1014903

La méthode 2 est incorrecte car vous effectuez une opération en deux étapes alors qu'elle pourrait être effectuée en une seule. Dans la méthode 2, vous vérifiez si la file d'attente est vide, puis plus tard (très bientôt, mais toujours plus tard), essayez d'obtenir l'élément. Que faire si vous avez deux threads tirant des éléments de la file d'attente? Le get () peut toujours échouer avec une file d'attente vide. Que faire si un élément est ajouté à la file d'attente après avoir vérifié qu'il était vide? Ce sont le genre de minuscules fenêtres d'opportunité où les bogues se glissent dans du code simultané.

Faites-le en une seule étape, c'est de loin le meilleur choix.

import Queue

q = Queue.Queue()

try:
    task = q.get(False)
except Queue.Empty:
    # Handle empty queue here
    pass
else:
    # Handle task here and call q.task_done()

Ne vous bloquez pas sur "les exceptions devraient être des erreurs". Les exceptions sont simplement un autre canal de communication, utilisez-les. Utilisez la clause "else" ici pour réduire la portée de la clause d'exception.

34
Ned Batchelder

S'il s'agit de code multithread/multiprocess (comme c'est de toute façon une bonne raison d'utiliser des files d'attente), alors certainement la méthode 1. Entre l'appel q.empty() et l'appel q.get(), le Jack of Hearts pourrait ont volé vos tartes!

4
Daren Thomas

Un argument est que la méthode 1 est erronée car la file d'attente vide n'est pas une erreur et ne doit donc pas être gérée à l'aide de l'exception Queue.Empty

Une exception n'est pas nécessairement une "erreur", c'est un mécanisme général de contrôle de flux, et est en effet utilisée de cette façon dans quelques cas (SysExit, StopIteration, etc.).

La bonne question ici est: quel sera le cas le plus courant - file d'attente vide ou non vide. À moins que vous ne le sachiez avec certitude, vous voulez demander à AskBeforeYouLeap, car c'est très probablement beaucoup moins cher.

2