Decorating class member functions

A

Andy Terrel

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

Andy Terrel

Oh I should mention the decorator needs to have some notion of state
(such as with the above class)
 
V

Virgil Dupras

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.
 
V

Virgil Dupras

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.
 
V

Virgil Dupras

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
 
A

Andy Terrel

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.
 
J

Jorge Godoy

Andy Terrel said:
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
 
A

Andy Terrel

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
 
S

Steven D'Aprano

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."

spam spam spam
'It has beautiful plummage.'spam spam spam spam spam
'It has beautiful plummage.'
 
P

Peter Otten

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

7stud

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
 
R

Rhamphoryncus

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!
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,008
Latest member
HaroldDark

Latest Threads

Top