FAQ
Okay does anyone know how to decorate class member functions?

The following code gives me an error:

Traceback (most recent call last):
File "decorators2.py", line 33, in <module>
s.update()
File "decorators2.py", line 13, in __call__
retval = self.fn.__call__(*args,**kws)
TypeError: update() takes exactly 1 argument (0 given)

------------------


#! /usr/bin/env python

class Bugger (object):
def __init__ (self, module, fn):
self.module = module
self.fn = fn

def __call__ (self,*args, **kws):
ret_val = self.fn(*args,**kws)
return ret_val

def instrument (module_name):
ret_val = lambda x: Bugger(module_name, x)
return ret_val

class Stupid:
def __init__(self):
self.val = 1

@instrument("xpd.spam")
def update(self):
self.val += 1


s = Stupid()
s.update()

Search Discussions

  • Andy Terrel at May 4, 2007 at 1:31 am
    Oh I should mention the decorator needs to have some notion of state
    (such as with the above class)
  • Virgil Dupras at May 4, 2007 at 1:33 am

    On May 3, 9:21 pm, Andy Terrel wrote:
    Okay does anyone know how to decorate class member functions?

    The following code gives me an error:

    Traceback (most recent call last):
    File "decorators2.py", line 33, in <module>
    s.update()
    File "decorators2.py", line 13, in __call__
    retval = self.fn.__call__(*args,**kws)
    TypeError: update() takes exactly 1 argument (0 given)

    ------------------

    #! /usr/bin/env python

    class Bugger (object):
    def __init__ (self, module, fn):
    self.module = module
    self.fn = fn

    def __call__ (self,*args, **kws):
    ret_val = self.fn(*args,**kws)
    return ret_val

    def instrument (module_name):
    ret_val = lambda x: Bugger(module_name, x)
    return ret_val

    class Stupid:
    def __init__(self):
    self.val = 1

    @instrument("xpd.spam")
    def update(self):
    self.val += 1

    s = Stupid()
    s.update()
    A decorator is a function that takes one single parameter: a function.
    "instrument" must return a decorator.
  • Virgil Dupras at May 4, 2007 at 1:41 am

    On May 3, 9:33 pm, Virgil Dupras wrote:
    On May 3, 9:21 pm, Andy Terrel wrote:


    Okay does anyone know how to decorate class member functions?
    The following code gives me an error:
    Traceback (most recent call last):
    File "decorators2.py", line 33, in <module>
    s.update()
    File "decorators2.py", line 13, in __call__
    retval = self.fn.__call__(*args,**kws)
    TypeError: update() takes exactly 1 argument (0 given)
    ------------------
    #! /usr/bin/env python
    class Bugger (object):
    def __init__ (self, module, fn):
    self.module = module
    self.fn = fn
    def __call__ (self,*args, **kws):
    ret_val = self.fn(*args,**kws)
    return ret_val
    def instrument (module_name):
    ret_val = lambda x: Bugger(module_name, x)
    return ret_val
    class Stupid:
    def __init__(self):
    self.val = 1
    @instrument("xpd.spam")
    def update(self):
    self.val += 1
    s = Stupid()
    s.update()
    A decorator is a function that takes one single parameter: a function.
    "instrument" must return a decorator.
    Oh wait, I just embarrassed myself. Nevermind my last post.
  • Virgil Dupras at May 4, 2007 at 2:03 am

    On May 3, 9:21 pm, Andy Terrel wrote:
    Okay does anyone know how to decorate class member functions?

    The following code gives me an error:

    Traceback (most recent call last):
    File "decorators2.py", line 33, in <module>
    s.update()
    File "decorators2.py", line 13, in __call__
    retval = self.fn.__call__(*args,**kws)
    TypeError: update() takes exactly 1 argument (0 given)

    ------------------

    #! /usr/bin/env python

    class Bugger (object):
    def __init__ (self, module, fn):
    self.module = module
    self.fn = fn

    def __call__ (self,*args, **kws):
    ret_val = self.fn(*args,**kws)
    return ret_val

    def instrument (module_name):
    ret_val = lambda x: Bugger(module_name, x)
    return ret_val

    class Stupid:
    def __init__(self):
    self.val = 1

    @instrument("xpd.spam")
    def update(self):
    self.val += 1

    s = Stupid()
    s.update()
    Second attempt. After some fuss around, I think it's just not possible
    to have a class instance as a decorator. the self gets lost in
    translation.

    #! /usr/bin/env python
    class Caller(object):
    def __init__ (self, fn):
    self.fn = fn

    def __call__(self, *args, **kwargs):
    print 'Caller calling!', repr(args)
    return self.fn(*args, **kwargs)

    def mydecorator(f):
    return Caller(f)

    class Stupid:
    def __init__(self):
    self.val = 1

    @mydecorator
    def update(self):
    self.val += 1

    s = Stupid()
    s.update()

    Caller calling! ()
    Traceback (most recent call last):
    File "/Users/hsoft/Desktop/foo.py", line 22, in ?
    s.update()
    File "/Users/hsoft/Desktop/foo.py", line 8, in __call__
    return self.fn(*args, **kwargs)
    TypeError: update() takes exactly 1 argument (0 given)

    But why do you want to use a class? If you want to have a decorator
    with argument, you only need to have something like:

    def instrument(module):
    def decorator(f):
    def wrapper(*args, **kwargs):
    print module
    return f(*args, **kwargs)
    return wrapper
    return decorator
  • Andy Terrel at May 4, 2007 at 2:28 am
    I just need to keep the state around. I make a call to some function
    that is pretty expensive so I want to save it as a member during the
    __init__ of the decorator.

    Yeah I'm afraid it can't be done either, that's why I asked the group.
  • Jorge Godoy at May 4, 2007 at 2:38 am

    Andy Terrel <andy.terrel at gmail.com> writes:

    I just need to keep the state around. I make a call to some function
    that is pretty expensive so I want to save it as a member during the
    __init__ of the decorator.

    Yeah I'm afraid it can't be done either, that's why I asked the group.
    Have you looked at memoize decorators? They seem to do what you want.
    There are examples at the Python website (some link in there, I'm
    sorry...).

    This will give you lots of resources, including full recipes and
    comments from the Python cookbook:
    http://www.google.com.br/search?q=python+decorator+memoize


    --
    Jorge Godoy <jgodoy at gmail.com>
  • Andy Terrel at May 4, 2007 at 2:39 am
    not quite as elegant but here is a workaround... Thanks Virgil for
    taking some time to think about it.

    ---

    class Bugger (object):
    def __init__ (self, module):
    print "Entering __init__"
    self.module = module
    self.verb = 0

    def instrument (module_name):
    def wrapper(f):
    def _wrap(*args,**kws):
    ret_val = f(*args,**kws)
    return ret_val
    return _wrap
    b = Bugger(module_name)
    if b.verb == 0:
    ret_val = wrapper
    else:
    ret_val = lambda x:x
    return ret_val

    class Stupid:
    def __init__(self):
    self.val = 1

    @instrument("spam")
    def update(self):
    self.val += 1


    s = Stupid()
    s.update()
    s.update()
    s.update()
    s.update()
    print s.val
  • Steven D'Aprano at May 4, 2007 at 3:50 am

    On Thu, 03 May 2007 19:28:52 -0700, Andy Terrel wrote:

    I just need to keep the state around. I make a call to some function
    that is pretty expensive so I want to save it as a member during the
    __init__ of the decorator.

    Yeah I'm afraid it can't be done either, that's why I asked the group.
    You can do it if you give up on using the decorator syntax.

    (Now that I've said that, some clever bunny will show me how to do it.)

    def make_decorator(n):
    def addspam(fn):
    def new(*args):
    print "spam " * n
    return fn(*args)
    return new
    return addspam


    class Parrot(object):
    def __init__(self, count=3):
    from new import instancemethod as im
    self.describe = im(make_decorator(count)(self.__class__.describe), self)
    def describe(self):
    return "It has beautiful plummage."

    bird = Parrot()
    bird.describe()
    spam spam spam
    'It has beautiful plummage.'

    bird = Parrot(5)
    bird.describe()
    spam spam spam spam spam
    'It has beautiful plummage.'



    --
    Steven D'Aprano
  • Peter Otten at May 4, 2007 at 4:34 am

    Andy Terrel wrote:

    Okay does anyone know how to decorate class member functions?

    The following code gives me an error:

    Traceback (most recent call last):
    File "decorators2.py", line 33, in <module>
    s.update()
    File "decorators2.py", line 13, in __call__
    retval = self.fn.__call__(*args,**kws)
    TypeError: update() takes exactly 1 argument (0 given)

    ------------------


    #! /usr/bin/env python

    class Bugger (object):
    def __init__ (self, module, fn):
    self.module = module
    self.fn = fn

    def __call__ (self,*args, **kws):
    ret_val = self.fn(*args,**kws)
    return ret_val

    def instrument (module_name):
    ret_val = lambda x: Bugger(module_name, x)
    return ret_val

    class Stupid:
    def __init__(self):
    self.val = 1

    @instrument("xpd.spam")
    def update(self):
    self.val += 1


    s = Stupid()
    s.update()
    The problem is not that you are decorating a method but that you are trying
    to use a callable class instance as a method. For that to work the class
    has to implement the descriptor protocol, see

    http://users.rcn.com/python/download/Descriptor.htm

    class Bugger (object):
    def __init__ (self, module, fn, instance=None):
    self.module = module
    self.fn = fn
    self.instance = instance

    def __call__ (self, *args, **kws):
    print "calling %s.%s()" % (self.module, self.fn.__name__)
    if self.instance is not None:
    args = (self.instance,) + args
    ret_val = self.fn(*args, **kws)
    return ret_val

    def __get__(self, instance, class_):
    if instance is None:
    return self
    return Bugger(self.module, self.fn, instance)

    def instrument (module_name):
    ret_val = lambda x: Bugger(module_name, x)
    return ret_val

    class Stupid(object):
    @instrument("xpd.spam")
    def update(self):
    print "update()"

    s = Stupid()
    s.update()
    Stupid.update(s)

    Peter
  • Rhamphoryncus at May 4, 2007 at 5:19 pm

    On May 3, 10:34 pm, Peter Otten wrote:
    The problem is not that you are decorating a method but that you are trying
    to use a callable class instance as a method. For that to work the class
    has to implement the descriptor protocol, see

    http://users.rcn.com/python/download/Descriptor.htm

    class Bugger (object):
    def __init__ (self, module, fn, instance=None):
    self.module = module
    self.fn = fn
    self.instance = instance

    def __call__ (self, *args, **kws):
    print "calling %s.%s()" % (self.module, self.fn.__name__)
    if self.instance is not None:
    args = (self.instance,) + args
    ret_val = self.fn(*args, **kws)
    return ret_val

    def __get__(self, instance, class_):
    if instance is None:
    return self
    return Bugger(self.module, self.fn, instance)

    def instrument (module_name):
    ret_val = lambda x: Bugger(module_name, x)
    return ret_val

    class Stupid(object):
    @instrument("xpd.spam")
    def update(self):
    print "update()"

    s = Stupid()
    s.update()
    Stupid.update(s)
    I've been bitten by the "class instances as decorators won't get
    bound" bug many times. I had no idea there was a direct solution.
    Thanks!

    --
    Adam Olsen, aka Rhamphoryncus
  • 7stud at May 4, 2007 at 5:36 am

    On May 3, 7:21 pm, Andy Terrel wrote:
    Okay does anyone know how to decorate class member functions?

    The following code gives me an error:

    Traceback (most recent call last):
    File "decorators2.py", line 33, in <module>
    s.update()
    File "decorators2.py", line 13, in __call__
    retval = self.fn.__call__(*args,**kws)
    TypeError: update() takes exactly 1 argument (0 given)

    ------------------

    #! /usr/bin/env python

    class Bugger (object):
    def __init__ (self, module, fn):
    self.module = module
    self.fn = fn

    def __call__ (self,*args, **kws):
    ret_val = self.fn(*args,**kws)
    return ret_val

    def instrument (module_name):
    ret_val = lambda x: Bugger(module_name, x)
    return ret_val

    class Stupid:
    def __init__(self):
    self.val = 1

    @instrument("xpd.spam")
    def update(self):
    self.val += 1

    s = Stupid()
    s.update()
    As far as I can tell, the problem is that the decorator executes when
    the class is parsed, and at that time there is no self(the instance
    object). The decorator produces a callable Bugger object, but the
    callable object has no way to get self when s.update() is called.
    Normally when you call a class function, like s.update(), the
    __get__() method in the 'update' function object is called (all
    function objects have a __get__() method and therefore are
    descriptors). Then __get__() creates a method object out of the
    function object(update), and python automatically passes the instance
    object to the method object. However, the Bugger object does not have
    a __get__() method, so no method object is created when the Bugger
    object is called, and therefore self is not automatically passed to
    the Bugger object.

    The following solution adds a __get__() method to the Bugger object.
    Python automatically passes the instance object to the __get__()
    method, and the solution stores the instance in the Bugger object.
    Then __call__ is defined to send the instance object to update().

    class Bugger (object):
    def __init__ (self, module, fn):
    self.module = module
    self.fn = fn

    def __call__ (self,*args, **kws):
    ret_val = self.fn(self.obj, *args,**kws)
    return ret_val

    def __get__(descr, inst, instCls=None):
    descr.obj = inst
    return descr

    def instrument (module_name):
    ret_val = lambda func: Bugger(module_name, func)
    return ret_val

    class Stupid(object):
    def __init__(self):
    self.val = 1

    @instrument("xpd.spam")
    def update(self):
    self.val += 1

    s = Stupid()
    s.update()
    s.update()
    s.update()
    print s.val

    --output:--
    4
  • Andy Terrel at May 4, 2007 at 2:01 pm
    Thanks Peter and 7stud. That is the solution that really works for
    me.

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
grouppython-list @
categoriespython
postedMay 4, '07 at 1:21a
activeMay 4, '07 at 5:19p
posts13
users7
websitepython.org

People

Translate

site design / logo © 2022 Grokbase