Why is class decorator on method loosing self?

C

c james

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

George Sakkis

c said:
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
 
C

c james

If I am reading this correctly you, are rebinding min_max in
CustomizeMeta using '__customize' as the attribute to identify the
member to work on.

Thank you. I think you are right, this is probably the best way to
implement what I intend for caching resource intensive processing.
 
G

George Sakkis

George said:
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:

An improvement to my previous hack: leave memoize as is in the cookbook
(extending decoratorargs) and add two lines to decoratorargs:

# This would usually be defined elsewhere
class decoratorargs(object):
def __new__(typ, *attr_args, **attr_kwargs):
def decorator(orig_func):
self = object.__new__(typ)
self.__init__(orig_func, *attr_args, **attr_kwargs)
if callable(self):
self._customize = lambda cls: MethodType(self, None,
cls)
return self
return decorator

Now you don't need memoizefunction and memoizemethod, but you still
need the customized metaclass (changed __customize to _customize; name
turns to a PITA sooner or later):

class CustomizeMeta(type):
def __init__(cls, name, bases,dict):
for attr,val in dict.iteritems():
if hasattr(val, '_customize'):
setattr(cls, attr, val._customize(cls))


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

@memoize(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"

@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])


George
 
G

George Sakkis

George said:
Now you don't need memoizefunction and memoizemethod, but you still
need the customized metaclass (changed __customize to _customize; name
turns to a PITA sooner or later):

There was supposed to be a "mangling" after "name" (see, it's hard to
even spell it out correctly, let alone use it <wink>).

George
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,733
Messages
2,569,440
Members
44,832
Latest member
GlennSmall

Latest Threads

Top