Why is class decorator on method loosing self?

Discussion in 'Python' started by c james, Nov 21, 2006.

  1. c james

    c james Guest

    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)
     
    c james, Nov 21, 2006
    #1
    1. Advertising

  2. 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
     
    George Sakkis, Nov 21, 2006
    #2
    1. Advertising

  3. c james

    c james Guest

    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.

    to George Sakkis wrote:
    >
    > 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:
    >


    > George
    >
     
    c james, Nov 21, 2006
    #3
  4. George Sakkis wrote:

    > 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
     
    George Sakkis, Nov 21, 2006
    #4
  5. George Sakkis wrote:

    > 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
     
    George Sakkis, Nov 21, 2006
    #5
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Ralf W. Grosse-Kunstleve
    Replies:
    16
    Views:
    595
    Lonnie Princehouse
    Jul 11, 2005
  2. Mr. SweatyFinger
    Replies:
    2
    Views:
    2,030
    Smokey Grindel
    Dec 2, 2006
  3. Yuccaplant
    Replies:
    1
    Views:
    293
    Peter Otten
    Mar 29, 2010
  4. Replies:
    6
    Views:
    117
    Vivek
    Jan 25, 2006
  5. Replies:
    7
    Views:
    165
Loading...

Share This Page