web-dev-qa-db-fra.com

RecursionError: dépassement de la profondeur de récursivité maximale

J'espère que ce n'est pas un doublon, je m'excuse si c'est le cas, mais j'ai fait quelques recherches sur google et fait le tour du débordement de la pile et je n'ai encore rien trouvé ...

MCVE

Je comprends que si une fonction continue de s'appeler elle-même, cela ne peut pas continuer indéfiniment sans débordement de pile, et donc une erreur est déclenchée après une certaine limite. Par exemple:

def foo():
    return foo()

foo()

Cela donne lieu à l'erreur suivante:

RecursionError: maximum recursion depth exceeded

Cependant, si j'écris une fonction telle que la suivante:

def count(n):
    if n == 0:
        return 0
    else:
        return count(n-1)+1

count(1000)

J'obtiens une erreur légèrement différente:

RecursionError: maximum recursion depth exceeded in comparison

La question

À quoi se réfère "en comparaison" dans l'erreur ci-dessus. Je suppose que ce que je demande, c'est quelle est la différence entre ces deux situations, qui donne lieu à deux erreurs différentes.

9
Tim

Lorsqu'un RecursionError est levé, l'interpréteur python peut également vous proposer le contexte de l'appel qui a provoqué l'erreur. Cela ne sert qu'au débogage, pour vous donner un indice où dans votre code, vous devez regarder afin de résoudre le problème.

Voir par exemple cette configuration d'appel circulaire str- qui mène à un message différent:

>>> class A:
...     def __str__(self):
...         return str(self.parent)
>>> a = A()
>>> a.parent = a
>>> str(a)
RecursionError: maximum recursion depth exceeded while calling a Python object

Il n'y a aucune documentation de ce comportement sur la discussion du problèmeRecursionError a été introduit, mais vous pouvez simplement rechercher dans le code cpython les occurrences de Py_EnterRecursiveCall . Ensuite, vous pouvez voir les contextes réels qui seront retournés en fonction de l'endroit où l'erreur est déclenchée:

Py_EnterRecursiveCall(" while encoding a JSON object")
Py_EnterRecursiveCall(" while pickling an object")
Py_EnterRecursiveCall(" in __instancecheck__")
Py_EnterRecursiveCall(" in __subclasscheck__")
Py_EnterRecursiveCall(" in comparison")
Py_EnterRecursiveCall(" while getting the repr of an object")
Py_EnterRecursiveCall(" while getting the str of an object")
Py_EnterRecursiveCall(" while calling a Python object")
Py_EnterRecursiveCall("while processing _as_parameter_") # sic
# .. and some more that I might have missed
6
Arne

J'ai joué avec et j'ai trouvé des résultats intéressants.

Comme nous le savons:

def foo():
    foo()

Donne lieu à

RecursionError: maximum recursion depth exceeded

Ce que j'ai trouvé était

def bar():
    if False:
        return 0
    else:
        bar()

def baz():
    if True:
        baz()
    else:
        return 0

bar() et baz() donnent lieu à

RecursionError: maximum recursion depth exceeded

Puis

def Ding():
    if 1 == 2:
        return 0
    else:
        Ding()

def dong():
    if 1 != 2:
        dong()
    else:
        return 0

Ding() et dong() donnent lieu à

RecursionError: maximum recursion depth exceeded in comparison

Mon intuition ici est que python sait que vous faites une comparaison en utilisant les comparateurs =,!,<,> Et que cette comparaison n'atteint jamais la condition de "cas de base" (dans les limites de la profondeur maximale) Donc python vous permet de savoir que votre comparaison ne converge jamais pour remplir la condition.).

Cette utilité commence à s'effondrer lorsque vous essayez

def oops():
    if 1 == 2:
        oops()
    else:
        oops()

Mais au final python ne peut être utile que pour les messages d'erreur.

2
Jake