Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Maximum recursion depth reached faster when using functools.lru_cache

I've been playing around with memoization and recursion in python 3.3

Ignoring the fact that python is the wrong language to be doing this in, I've found that I get inconsistent results between using functools.lru_cache to memoize, and not using functools.lru_cache

I'm not changing the recursion limit - it stays at the default, which for me is 1000.

To test the problem, I've written up a simple recursive function to sum numbers from 1 through i

#!/usr/bin/python

def sumtil(i):
"""Recursive function to sum all numbers from 1 through i"""

    # Base case, the sum of all numbers from 1 through 1 is 1... 
    if i == 1:
        return 1
    else:
        return i+sumtil(i-1)

# This will not throw an exception
sumtil(998)

# This will throw an exception
sumtil(999)

Running this function normally, I can run sumtil(998) comfortably without hitting the recursion limit. sumtil(999) or above will throw an exception.

However, if I try decorating this function with @functools.lru_cache(), the recursion limit exception is thrown 3 times earlier, when running sumtil(333)

#!/usr/bin/python

import functools 

@functools.lru_cache(maxsize=128)
def sumtil(i):
    """Recursive function to sum all numbers from 1 through i"""

    # Base case, the sum of all numbers from 1 through 1 is 1... 
    if i == 1:
        return 1
    else:
        return i+sumtil(i-1)

# This will not throw an exception
sumtil(332)

# This will throw an exception
sumtil(333)

Being that 332*3 = 996, but 333*3 = 999, it appears to me that the lru_cache decorator is causing each level of recursion in my function to become three levels of recursion.

Why do I get three times as many levels of recursion when using functools.lru_cache to memoize a function?

like image 470
Kiirani Avatar asked Mar 06 '13 04:03

Kiirani


People also ask

How do you fix maximum recursion depth exceeded?

The “maximum recursion depth exceeded in comparison” error is raised when you try to execute a function that exceeds Python's built in recursion limit. You can fix this error by rewriting your program to use an iterative approach or by increasing the recursion limit in Python.

What is the maximum recursion depth?

The maximum recursion depth in Python is 1000. You can change the limit by calling sys. setrecursionlimit() method. Consider this a dangerous action!

How do you fix a recursive error?

Try increasing the recursion limit ( sys. setrecursionlimit ) or re-writing your code without recursion. Return the current value of the recursion limit, the maximum depth of the Python interpreter stack. This limit prevents infinite recursion from causing an overflow of the C stack and crashing Python.

What is the maximum recursion depth in Java?

Typically, the stack will overflow after between 6,000 and 7,000 steps.


1 Answers

Because a decorator is an extra function, so it "uses" one level in the stack. Example:

>>> def foo(f):
...   def bar(i):
...     if i == 1:
...       raise Exception()
...     return f(i)
...   return bar
...
>>> @foo
... def sumtil(i):
...     if i == 1:
...         return 1
...     else:
...         return i+sumtil(i-1)
...
>>> sumtil(3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in bar
  File "<stdin>", line 6, in sumtil
  File "<stdin>", line 5, in bar
  File "<stdin>", line 6, in sumtil
  File "<stdin>", line 4, in bar
Exception
>>>

Besides, if the decorator uses argument packing/unpacking, then an extra level is used (though I'm not knowledgeable enough about the Python runtime to explain why that happens).

def foo(f):
  def bar(*args,**kwargs):
    return f(*args,**kwargs)
  return bar

Max. recursion depth exceeded:

  • undecorated: 1000
  • w/o packing: 500
  • with packing: 334
like image 56
mgibsonbr Avatar answered Oct 19 '22 11:10

mgibsonbr