Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AttributeError: 'str' object has no attribute '__module__'

Tags:

python

caching

I have been testing out this caching method/code: http://code.activestate.com/recipes/498245-lru-and-lfu-cache-decorators/?c=15348

and in some cases, I get this (or similar) error: "AttributeError: 'str' object has no attribute 'module'"

and here are code examples, these work fine:

if __name__ == '__main__':
@lru_cacheItem(maxsize=20)
def f(x, y):
    return 3*x+y

domain = range(5)
from random import choice
for i in range(1000):
    r = f(choice(domain), choice(domain))

print('Hits:{0}'.format(f.hits), 'Misses:{0}'.format(f.misses))

@lfu_cacheItem(maxsize=20)
def f(x, y):
    return 3*x+y

domain = range(5)
from random import choice
for i in range(1000):
    r = f(choice(domain), choice(domain))

print('Hits:{0}'.format(f.hits), 'Misses:{0}'.format(f.misses))  


@lru_cacheItem(maxsize=20)
def myString(a, b):
    return '{0} and {1}'.format(a, b)

a = 'crap'
b = 'shit'
for i in range(1000):
    r = myString(a, b)

print('Hits:{0}'.format(myString.hits), 'Misses:{0}'.format(myString.misses)) 

and this does not:

if __name__ == '__main__':    
class P4client(object):
    def __init__(self):
        pass

    def checkFileStats(self, filePath):
        results = 'The filepath: {0}'.format(filePath)
        print results
        return results

p4client = P4client()

filePath = (r"C:\depot\tester.tga")

@lfu_cacheItem            
def p4checkFileStats(filePath):
    '''Will cache the fstats return'''
    p4checkResults = p4client.checkFileStats(filePath)
    return p4checkResults    

p4checkFileStats(filePath)

I am not sure how to fix this ... it appears to be an issue in functools, I assume somehow do to that fact I am calling a class/method within the function I am wrapping?

like image 320
Jonathan Kimball Galloway Avatar asked Aug 28 '13 16:08

Jonathan Kimball Galloway


1 Answers

@lfu_cacheItem            
def p4checkFileStats(filePath):

You are missing parenthesis here:

@lfu_cacheItem()            
def p4checkFileStats(filePath):

All decorators which expect "options", i.e. that you can use as:

@decorator(a=Something, b=Other, ...)
def the_function(...):

Must always be called when decorating, even if you do not provide arguments:

@decorator()
def the_function(...):

Why you wonder? Well, first of all remember that decorators are normal functions that accept a function as argument:

In [1]: def hooray(func):
   ...:     print("I'm decorating function: {.__name__}".format(func))
   ...:     return func

In [2]: @hooray
   ...: def my_function(): pass
I'm decorating function: my_function

As you can see hooray was called. In fact this is what really happens when using a decorator:

In [3]: def my_function(): pass
   ...: my_function = hooray(my_function)
   ...: 
I'm decorating function: my_function

Now, if you want to pass options to the decorator you can create a function that returns a decorator. This is exactly what happens with lfu_cache from the recipe you link:

def lfu_cache(maxsize=100):
    # ...
    def decorating_function(user_function):
        # ...
    return decorating_function

Now here you can see that lfu_cache is really a function. this function creates a decorator, called decorating_function and returns it. This means that when calling:

@lfu_cache(maxsize=20)
def my_function(): pass

This is what happens:

def my_function(): pass
decorator = lfu_cache(maxsize=20)
my_function = decorator(my_function)

As you can see first lfu_cache is called, and returns a decorator. Afterwards the decorator is called to decorate the function. What happens if you forget the parenthesis? What does this:

@lfu_cache
def my_function(): pass

do?

Pretty simple, it uses lfu_cache as a simple decorator:

def my_function(): pass
my_function = lfu_cache(my_function)

But this is bad! You passed a function as maxsize parameter and the value returned by lfu_cache is the decorating_function of before!

to learn more about decorators read this So answer.

like image 125
Bakuriu Avatar answered Oct 20 '22 16:10

Bakuriu