Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RecursionError: maximum recursion depth exceeded in comparison

I hope that this is not a duplicate, I apologise if so, but have done some googling and looking around stack overflow and not found anything as yet...

MCVE

I understand that if a function keeps calling itself, this can't keep happening indefinitely without a stack overflow, and so an error is raised after a certain limit. For example:

def foo():
    return foo()

foo()

This gives rise to the following error:

RecursionError: maximum recursion depth exceeded

However, if I write a function such as the following:

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

count(1000)

I get a slightly different error:

RecursionError: maximum recursion depth exceeded in comparison

The question

What is the "in comparison" referring to in the above error. I guess what I'm asking is what is difference between these two situations, that gives rise to two different errors.

like image 638
tim-mccurrach Avatar asked Oct 18 '18 11:10

tim-mccurrach


2 Answers

I played around with it and found some interesting results.

As we know:

def foo():
    foo()

Gives rise to

RecursionError: maximum recursion depth exceeded

What I found was

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

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

Both bar() and baz() give rise to

RecursionError: maximum recursion depth exceeded

And then

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

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

Both ding() and dong() give rise to

RecursionError: maximum recursion depth exceeded in comparison

My intuition here is that python knows you are doing a comparison using the comparators =,!,<,> and that this comparison never reaches the 'base case' condition (within the limits of the maximum depth). So python is letting you know that your comparison never converges to meet the condition.

This helpfulness starts to break down when you try

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

But in the end python can only be so helpful with error messages.

like image 77
Jake Avatar answered Sep 16 '22 15:09

Jake


When a RecursionError is raised, the python interpreter may also offer you the context of the call that caused the error. This only serves for debugging, to give you a hint where in your code you should look in order to fix the problem.

See for example this circular str-call setup that leads to a different message:

>>> 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

There is no documentation of this behaviour on the issue discussion where RecursionError was introduced, but you can just search the cpython code for occurences of Py_EnterRecursiveCall. Then you can see the actual contexts that will be returned depending on where the error is raised:

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
like image 28
Arne Avatar answered Sep 18 '22 15:09

Arne