Why is class decorator on method loosing self?

George Sakkis george.sakkis at gmail.com
Tue Nov 21 14:04:23 EST 2006


c james wrote:

> I want to use the LRU decorator posted at
> http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/498110
> on a class method.  However, it complains about missing arguments. The
> missing argument is `self`.  I could use @classmethod but what I really
> need is an instance method.  I don't see how and was hoping someone else
> might know the way.
>
> Here is an example with taking that recipe as lru.py
>
> import lru
>
> class Foo(object):
>      def banner(self):
>          print "Testing method"
>
>      @memoize(3)
>      def min_max(self, sequence):
>          self.banner()
>          return min(sequence), max(sequence)
>
> foo = Foo()
> print foo.min_max([9,7,5,3,1])
>
>
> Traceback (most recent call last):
> ...
>    File "lru.py", line 48, in __call__
>      value = self.func(*args, **kwargs)
> TypeError: min_max() takes exactly 2 arguments (1 given)


I don't think you can make it work without resorting to metaclass
magic. At the point of decoration min_max is still a function, not a
method, because class Foo has not been created yet. Here's a way to do
it with a custom metaclass; whether you really want to do it is a
different matter:

First off, remove the decoratorargs class and have memoize inherit from
object:

class memoize(object):
    # class body stays the same

Then add the following:


# this is general enough to be moved to a separate module
class CustomizeMeta(type):
    def __init__(cls, name, bases,dict):
        for attr,val in dict.iteritems():
            if hasattr(val, '__customize'):
                setattr(cls, attr, getattr(val,'__customize')(cls))


def memoizefunction(*args, **kwds):
    return lambda func: memoize(func, *args, **kwds)


def memoizemethod(*args, **kwds):
    from types import MethodType
    def wrapper(func):
        func.__customize = lambda cls: \
                    MethodType(memoize(func,*args,**kwds), None, cls)
        return func
    return wrapper

#==== examples =============================================

@memoizefunction(3)
def fib(n):
    return (n > 1) and (fib(n - 1) + fib(n - 2)) or 1


class Foo(object):
    __metaclass__ = CustomizeMeta

    def __init__(self, i): self._i = i

    def banner(self):
        print "Testing method"

    @memoizemethod(3)
    def min_max(self, sequence):
        self.banner()
        return min(sequence), max(sequence)

foo = Foo()
print foo.min_max([9,7,5,3,1])


George




More information about the Python-list mailing list