Can I use decorators to manipulate return type or create methods?

W

WakeBdr

I'm writing a class that will query a database for some data and return
the result to the caller. I need to be able to return the result of
the query in several different ways: list, xml, dictionary, etc. I was
wondering if I can use decorators to accomplish this.

For instance, I have the following method

def getUsers(self, params):
return users.query(dbc)

To get the appropriate return types, I also have these methods. I have
these convenience methods for every query method in my class.

def getUsersAsXML(self, params):
return self._toXML(self.getUsers(params))
def getUsersAsDict(self, params):
return self._toDict(self.getUsers(params))
def getUsersAsList(self, params):
return self._toList(self.getUsers(params))

Instead of creating these three methods for every query method, is
there a way to use decorators to manipulate the return type. I'd still
like to have the caller use getUsersAsXML, I just don't want to write
the AsXML methods for every query method. So the decorator would
essentially create the convenience methods instead of me coding them.

One solution that I don't want to use is passing a variable into the
query method that determines the return type. This is what I don't
want to do.
def getUsers(self, params, returnType):

Any ideas on how I can accomplish this?

thanks
 
C

Chris Mellon

I'm writing a class that will query a database for some data and return
the result to the caller. I need to be able to return the result of
the query in several different ways: list, xml, dictionary, etc. I was
wondering if I can use decorators to accomplish this.

For instance, I have the following method

def getUsers(self, params):
return users.query(dbc)

To get the appropriate return types, I also have these methods. I have
these convenience methods for every query method in my class.

def getUsersAsXML(self, params):
return self._toXML(self.getUsers(params))
def getUsersAsDict(self, params):
return self._toDict(self.getUsers(params))
def getUsersAsList(self, params):
return self._toList(self.getUsers(params))

Instead of creating these three methods for every query method, is
there a way to use decorators to manipulate the return type. I'd still
like to have the caller use getUsersAsXML, I just don't want to write
the AsXML methods for every query method. So the decorator would
essentially create the convenience methods instead of me coding them.

One solution that I don't want to use is passing a variable into the
query method that determines the return type. This is what I don't
want to do.
def getUsers(self, params, returnType):

Any ideas on how I can accomplish this?


You can't do it as fully magically as I'd like, because at the time
decorators are run, the methods are just functions and aren't bound as
methods yet (so you can't automagically add methods to the class, for
example - you'd need to do that after the class definition finishes
executing). You *could* decorate all the functions you want to have
special return types, then (after the class definition) loop through
those to generate the extra return funcs.

You can do it with a minimum of boilerplate this way:

from functools import wraps #only in 2.5, you can do this by hand in 2.4
#wrapped is the function we're calling and returning as XML
#xmlfunc is the stub function we're replacing
def returnXML(wrapped):
def f(xmlfunc):
@wraps(xmlfunc)
def xmlMethod(self):
return self.asXML(wrapped(self))
return xmlMethod
return f

class T(object):
def getUser(self):
return "user"
def asXML(self, data):
return "<xml>%s</xml>"%(data)
@returnXML(getUser)
def getUserAsXML(self):pass



t = T()
print t.getUserAsXML()
 
D

Diez B. Roggisch

WakeBdr said:
I'm writing a class that will query a database for some data and return
the result to the caller. I need to be able to return the result of
the query in several different ways: list, xml, dictionary, etc. I was
wondering if I can use decorators to accomplish this.

For instance, I have the following method

def getUsers(self, params):
return users.query(dbc)

To get the appropriate return types, I also have these methods. I have
these convenience methods for every query method in my class.

def getUsersAsXML(self, params):
return self._toXML(self.getUsers(params))
def getUsersAsDict(self, params):
return self._toDict(self.getUsers(params))
def getUsersAsList(self, params):
return self._toList(self.getUsers(params))

Instead of creating these three methods for every query method, is
there a way to use decorators to manipulate the return type. I'd still
like to have the caller use getUsersAsXML, I just don't want to write
the AsXML methods for every query method. So the decorator would
essentially create the convenience methods instead of me coding them.

One solution that I don't want to use is passing a variable into the
query method that determines the return type. This is what I don't
want to do.
def getUsers(self, params, returnType):

Any ideas on how I can accomplish this?

Use a metaclass.

class Magic(type):
def __new__(cls, name, bases, d):
for name, function in d.items():
try:
function._marked
def toXML(self, *args, **kwargs):
return self._toXML(function(self, *args, **kwargs))
def toFOO(self, *args, **kwargs):
return self._toFOO(function(self, *args, **kwargs))
d[name + "XML"] = toXML
d[name + "FOO"] = toFOO
except AttributeError:
pass
return type(name, bases, d)


def mark(f):
f._marked = True
return f

class MyClass(object):
__metaclass__ = Magic

def _toXML(self, value):
return "Look Ma, its XML! %r" % value

def _toFOO(self, value):
return "Look Ma, its FOO! %r" % value


@mark
def someMethod(self):
return "Its the data, son"


o = MyClass()

print o.someMethod()
print o.someMethodXML()
print o.someMethodFOO()



Diez
 
P

Peter Otten

WakeBdr said:
I'm writing a class that will query a database for some data and return
the result to the caller. I need to be able to return the result of
the query in several different ways: list, xml, dictionary, etc. I was
wondering if I can use decorators to accomplish this.

For instance, I have the following method

def getUsers(self, params):
return users.query(dbc)

To get the appropriate return types, I also have these methods. I have
these convenience methods for every query method in my class.

def getUsersAsXML(self, params):
return self._toXML(self.getUsers(params))
def getUsersAsDict(self, params):
return self._toDict(self.getUsers(params))
def getUsersAsList(self, params):
return self._toList(self.getUsers(params))

Instead of creating these three methods for every query method, is
there a way to use decorators to manipulate the return type. I'd still
like to have the caller use getUsersAsXML, I just don't want to write
the AsXML methods for every query method. So the decorator would
essentially create the convenience methods instead of me coding them.

One solution that I don't want to use is passing a variable into the
query method that determines the return type. This is what I don't
want to do.
def getUsers(self, params, returnType):

Any ideas on how I can accomplish this?

Here's an odd approach, entirely based on naming conventions:

from operator import attrgetter

class Composer(object):
def __getattr__(self, name):
prefix, delim, suffix = name.rpartition("_as_")
if prefix and suffix:
cls = self.__class__
inner = attrgetter(prefix)
outer = attrgetter(delim + suffix)
def wrapped(self, *args):
return outer(self)(inner(self)(*args))
setattr(cls, name, wrapped)
return getattr(self, name)
raise AttributeError("sorry, no %r" % name)

class A(Composer):
def _as_xml(self, obj):
return "as_xml(%s)" % (obj,)
def _as_list(self, obj):
return "as_list(%s)" % (obj,)
def get_users(self):
return "get_users()"

class B(A):
def _as_list(self, obj):
return "AS_LIST(%s)" % (obj,)
def get_artist_as_a_young_man(self, name):
return "get_artist_as_a_young_man(name=%r)" % name

if __name__ == "__main__":
a = A()
b = B()
print a.get_users_as_list()
print b.get_users_as_list()
print a.get_users_as_xml()
print b.get_artist_as_a_young_man_as_xml("James")
print a.get_artist_as_a_young_man_as_xml("James") # AttributeError

Peter
 
W

WakeBdr

Diez,
What does the function._marked accomplish?
WakeBdr said:
I'm writing a class that will query a database for some data and return
the result to the caller. I need to be able to return the result of
the query in several different ways: list, xml, dictionary, etc. I was
wondering if I can use decorators to accomplish this.

For instance, I have the following method

def getUsers(self, params):
return users.query(dbc)

To get the appropriate return types, I also have these methods. I have
these convenience methods for every query method in my class.

def getUsersAsXML(self, params):
return self._toXML(self.getUsers(params))
def getUsersAsDict(self, params):
return self._toDict(self.getUsers(params))
def getUsersAsList(self, params):
return self._toList(self.getUsers(params))

Instead of creating these three methods for every query method, is
there a way to use decorators to manipulate the return type. I'd still
like to have the caller use getUsersAsXML, I just don't want to write
the AsXML methods for every query method. So the decorator would
essentially create the convenience methods instead of me coding them.

One solution that I don't want to use is passing a variable into the
query method that determines the return type. This is what I don't
want to do.
def getUsers(self, params, returnType):

Any ideas on how I can accomplish this?

Use a metaclass.

class Magic(type):
def __new__(cls, name, bases, d):
for name, function in d.items():
try:
function._marked
def toXML(self, *args, **kwargs):
return self._toXML(function(self, *args, **kwargs))
def toFOO(self, *args, **kwargs):
return self._toFOO(function(self, *args, **kwargs))
d[name + "XML"] = toXML
d[name + "FOO"] = toFOO
except AttributeError:
pass
return type(name, bases, d)


def mark(f):
f._marked = True
return f

class MyClass(object):
__metaclass__ = Magic

def _toXML(self, value):
return "Look Ma, its XML! %r" % value

def _toFOO(self, value):
return "Look Ma, its FOO! %r" % value


@mark
def someMethod(self):
return "Its the data, son"


o = MyClass()

print o.someMethod()
print o.someMethodXML()
print o.someMethodFOO()



Diez
 
D

Diez B. Roggisch

WakeBdr said:
Diez,
What does the function._marked accomplish?

Its a decorator that tells the metaclass which functions to provide with
a *XML/*FOO variant as well. I thought that was pretty obvious, given
the name "mark" and all that.

Diez
 
W

WakeBdr

Diez,
I get what that accomplishes now, but I'm having problems in my
implementation. I was able to write a standalone class that worked
correctly. However, in my code the class that I need to exhibit this
functionality inherits from another class. This seems to cause
problems when I attempt to implement you solution.

Let's say I have two classes that look like the following:

class Parent:
def getUsers(self, params):
raise 'Not implemented'

def _toXML(self, result):
return result.toXML()

def _toList(self, result):
return result.toList()

class Child(Parent):
def getUsers(self, params):
return users.query(dbc)


Caller object would say something like:
ch = Child()
ch.getUsersAsXML(params)

How would I implement your solution in this scenario. I've tried
"marking" the parent method, "marking" the child method, "marking"
both, but nothing seems to work.
Diez,
What does the function._marked accomplish?
WakeBdr said:
I'm writing a class that will query a database for some data and return
the result to the caller. I need to be able to return the result of
the query in several different ways: list, xml, dictionary, etc. I was
wondering if I can use decorators to accomplish this.

For instance, I have the following method

def getUsers(self, params):
return users.query(dbc)

To get the appropriate return types, I also have these methods. I have
these convenience methods for every query method in my class.

def getUsersAsXML(self, params):
return self._toXML(self.getUsers(params))
def getUsersAsDict(self, params):
return self._toDict(self.getUsers(params))
def getUsersAsList(self, params):
return self._toList(self.getUsers(params))

Instead of creating these three methods for every query method, is
there a way to use decorators to manipulate the return type. I'd still
like to have the caller use getUsersAsXML, I just don't want to write
the AsXML methods for every query method. So the decorator would
essentially create the convenience methods instead of me coding them.

One solution that I don't want to use is passing a variable into the
query method that determines the return type. This is what I don't
want to do.
def getUsers(self, params, returnType):

Any ideas on how I can accomplish this?

Use a metaclass.

class Magic(type):
def __new__(cls, name, bases, d):
for name, function in d.items():
try:
function._marked
def toXML(self, *args, **kwargs):
return self._toXML(function(self, *args, **kwargs))
def toFOO(self, *args, **kwargs):
return self._toFOO(function(self, *args, **kwargs))
d[name + "XML"] = toXML
d[name + "FOO"] = toFOO
except AttributeError:
pass
return type(name, bases, d)


def mark(f):
f._marked = True
return f

class MyClass(object):
__metaclass__ = Magic

def _toXML(self, value):
return "Look Ma, its XML! %r" % value

def _toFOO(self, value):
return "Look Ma, its FOO! %r" % value


@mark
def someMethod(self):
return "Its the data, son"


o = MyClass()

print o.someMethod()
print o.someMethodXML()
print o.someMethodFOO()



Diez
 
D

Diez B. Roggisch

WakeBdr said:
Diez,
I get what that accomplishes now, but I'm having problems in my
implementation. I was able to write a standalone class that worked
correctly. However, in my code the class that I need to exhibit this
functionality inherits from another class. This seems to cause
problems when I attempt to implement you solution.

You need to give a __metaclass__ to one of them, and I think they must
be new-style-classes.


Diez
 
W

WakeBdr

OK, I think I'm close now. I just can't get past this one error. Here
is my code, followed by the output produced when I run it.

class Magic(type):
def __new__(cls, name, bases, d):
for name, function in d.items():
try:
function._marked
print 'Class: %s' % cls
print 'Method: %s' % name
def toXML(self, *args, **kwargs):
return
self._toXML(function(self, *args, **kwargs))
def toList(self, *args, **kwargs):
return
self._toList(function(self, *args, **kwargs))
d[name+'XML'] = toXML
d[name+'List'] = toList
except AttributeError:
#traceback.print_exc()
pass
return type(name, bases, d)

def mark(f):
f._marked = True
return f

class test(object):

def _toXML(self, value):
return '<xml>%s</xml>' % value
def _toList(self, value):
return '<list>%s</list>' % value

class testtest(test):

__metaclass__ = Magic

@mark
def printData(self, data):
return 'child-%s' % data


t = testtest()
print t.printData('data')
print t.printDataXML('data')
print t.printDataList('data')

===========OUTPUT=========
Class: <class '__main__.Magic'>
Method: printData
child-data
Traceback (most recent call last):
File "test.py", line 43, in ?
print t.printDataXML('data')
File "test.py", line 11, in toXML
return self._toXML(function(self, *args, **kwargs))
TypeError: __new__() takes exactly 4 arguments (3 given)
 

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,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top