Function attribute used in recursion - does a global function attribute exist?

I have a function:

def fib(n, fib_d = {}):
     
   if n < 2:
      return 1
    
   if n not in fib_d:
     fib_d[n] = fib(n-2) + fib(n-1)
    
 
   return fib_d[n]

fib(1000)
# a big number returned in an instance

Then I try to create fib_d as an attribute of a function which data I could access:

def fib(n):
   fib.fib_d = {}
   
   if n < 2:
      return 1
    
   if n not in fib.fib_d:
     fib.fib_d[n] = fib(n-2) + fib(n-1)
    
 
   return fib.fib_d[n]
fib(10)
# returns 89 in an instance
fib(100)
# runs till I stop it

So I am guessing that fib_d is being created each time as an empty parameter, hence the slow calculation

I am wondering if I can have a global function attribute so that I could access it's content like:

fib.fib_d
# {2: 2, 3: 3, 4: 5, 5: 8, 6: 13, 7: 21, 8: 34, 9: 55, 10: 89, ..., n: m}

I tried:

def fib(n, fib.fib_d):
   ...

But that's not valid, I understand that probably functions attribute should be bound only to that function but I am curious if there is such thing as global function attributes or at least attribute that can be used in a recursion.


I would just use a cache, or lru_cache.

But to do it like you've asked for, the easiest way to do this is to define your function within a closure:

def make_fib():

   def fib(n):
   
       if n < 2:
          return 1
    
       if n not in fib.fib_d:
         fib.fib_d[n] = fib(n-2) + fib(n-1)
       return fib.fib_d[n]
   fib.fib_d = {}
   return fib

f= make_fib()
f(2000)

That way, we have an attribute of the function properly scoped.


it seems you just want to cache the results for each func

from functools import cache

@cache
def fib(n):
   if n < 2:
      return 1
   return fib(n-2) + fib(n-1)

You can use getattr to fetch the attribute if it already exists or create it otherwise:

def fib(n):
    if n < 2:
        return 1
    fib.cache = getattr(fib, 'cache', {})
    if n not in fib.cache:
        fib.cache[n] = fib(n-2) + fib(n-1)
    return fib.cache[n]

This is essentially just altering your unconditional creation of fib_d to only happen if it doesn't already exist; it's self-contained within the function and doesn't need an external definition or closure for the attribute.


You can check if the function already has an attribute fib_d:

def fib(n):
   if n < 2:
       return 1
   
   if not hasattr(fib, "fib_d"):
      fib.fib_d = {}
    
   if n not in fib.fib_d:
       fib.fib_d[n] = fib(n - 2) + fib(n - 1)
   
   return fib_d[n]

fib(1000)