By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
440,222 Members | 2,416 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 440,222 IT Pros & Developers. It's quick & easy.

Decorating class member functions

P: n/a
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()

May 4 '07 #1
Share this Question
Share on Google+
12 Replies


P: n/a
Oh I should mention the decorator needs to have some notion of state
(such as with the above class)

May 4 '07 #2

P: n/a
On May 3, 9:21 pm, Andy Terrel <andy.ter...@gmail.comwrote:
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.

May 4 '07 #3

P: n/a
On May 3, 9:33 pm, Virgil Dupras <hardcoded.softw...@gmail.comwrote:
On May 3, 9:21 pm, Andy Terrel <andy.ter...@gmail.comwrote:
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.

May 4 '07 #4

P: n/a
On May 3, 9:21 pm, Andy Terrel <andy.ter...@gmail.comwrote:
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

May 4 '07 #5

P: n/a
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.


May 4 '07 #6

P: n/a
Andy Terrel <an*********@gmail.comwrites:
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=py...orator+memoize
--
Jorge Godoy <jg****@gmail.com>
May 4 '07 #7

P: n/a
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

May 4 '07 #8

P: n/a
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

May 4 '07 #9

P: n/a
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
May 4 '07 #10

P: n/a
On May 3, 7:21 pm, Andy Terrel <andy.ter...@gmail.comwrote:
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

May 4 '07 #11

P: n/a
Thanks Peter and 7stud. That is the solution that really works for
me.

May 4 '07 #12

P: n/a
On May 3, 10:34 pm, Peter Otten <__pete...@web.dewrote:
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

May 4 '07 #13

This discussion thread is closed

Replies have been disabled for this discussion.