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

Automatic methods in new-style classes

P: n/a
Hey, I have the following code that has to send every command it
receives to a list of backends. Instead of:

class MultiBackend(object):
"""Renders to multiple backends"""

def __init__(self, backends):
self.backends = backends

def flush(self):
for b in self.backends:
b.flush()

def draw(self):
for b in self.backends:
b.draw()
I would like to write each method like:

flush = multimethod()

Is that possible using new-style classes?

Bert

Sep 29 '06 #1
Share this Question
Share on Google+
6 Replies


P: n/a
be******@yahoo.com wrote:
Hey, I have the following code that has to send every command it
receives to a list of backends. Instead of:

class MultiBackend(object):
"""Renders to multiple backends"""

def __init__(self, backends):
self.backends = backends

def flush(self):
for b in self.backends:
b.flush()

def draw(self):
for b in self.backends:
b.draw()
I would like to write each method like:

flush = multimethod()

Is that possible using new-style classes?
Not without a custom metaclass or the method name passed explicitly to
multimethod(). Here's a simpler approach that works with both new and
classic classes:

"""
>>class B(object):
.... def __init__(self, name):
.... self.name = name
.... def draw(self, x, y):
.... print "%s: draw(%s, %s)" % (self.name, x, y)
.... def flush(self):
.... print "%s: flush()" % self.name
>>m = MultiBackend([B("alpha"), B("beta")])
m.draw(1, 2)
alpha: draw(1, 2)
beta: draw(1, 2)
>>m.flush()
alpha: flush()
beta: flush()

"""

from operator import attrgetter

class MultiBackend(object):
def __init__(self, backends):
self._backends = backends
def __getattr__(self, name):
get = attrgetter(name)
def multi(self, *args, **kw):
for b in self._backends:
get(b)(*args, **kw)
setattr(self.__class__, name, multi)
return getattr(self, name)

if __name__ == "__main__":
import doctest
doctest.testmod()

Peter
Sep 29 '06 #2

P: n/a
be******@yahoo.com wrote:
Hey, I want to send commands to a list of backends:
How about something like:
class Forwards(object):

to_forward = set(['flush', 'read', 'write', 'close'])

def __init__(self, backends):
self.backends = backends

def forwarder(self, methodname):
def method(*args, **kwargs):
for b in self.backends:
getattr(b, methodname)(*args, **kwargs)
return forwarder

def __getattr__(self, name):
if name in self.to_forward:
return self.forwarder(name)
raise AttributeError("%r object has no attribute %r"
% (self.__class__.__name__, name))

--Scott David Daniels
sc***********@acm.org
Sep 29 '06 #3

P: n/a
Scott David Daniels wrote:
>
class Forwards(object):

to_forward = set(['flush', 'read', 'write', 'close'])

def __init__(self, backends):
self.backends = backends

def forwarder(self, methodname):
def method(*args, **kwargs):
for b in self.backends:
getattr(b, methodname)(*args, **kwargs)
return forwarder
^^^ return method # thinko.
>
def __getattr__(self, name):
if name in self.to_forward:
return self.forwarder(name)
raise AttributeError("%r object has no attribute %r"
% (self.__class__.__name__, name))
--Scott David Daniels
sc***********@acm.org
Sep 29 '06 #4

P: n/a
be******@yahoo.com wrote:
Hey, I have the following code that has to send every command it
receives to a list of backends.
<snip>
I would like to write each method like:

flush = multimethod()
Here's one way, using a metaclass:

class multimethod(object):
def transform(self, attr):
def dispatch(self, *args, **kw):
results = []
for b in self.backends:
results.append(getattr(b, attr)(*args, **kw))
return results
return dispatch

def multimethodmeta(name, bases, dict):
"""Transform each multimethod object into an actual method"""
for attr in dict:
if isinstance(dict[attr], multimethod):
dict[attr] = dict[attr].transform(attr)
return type(name, bases, dict)

class MultiBackend(object):
__metaclass__ = multimethodmeta
def __init__(self, backends):
self.backends = backends
add = multimethod()

class Foo(object):
def add(self, x, y):
print 'in Foo.add'
return x + y

class Bar(object):
def add(self, x, y):
print 'in Bar.add'
return str(x) + str(y)

m = MultiBackend([Foo(), Bar()])
print m.add(3, 4)

# Output:
in Foo.add
in Bar.add
[7, '34']

--Ben

Sep 29 '06 #5

P: n/a
Ben Cartwright wrote:
be******@yahoo.com wrote:
Hey, I have the following code that has to send every command it
receives to a list of backends.
<snip>
I would like to write each method like:

flush = multimethod()

Here's one way, using a metaclass:
Or if you don't mind a more verbose API, you may use a decorator
instead:

# comment out if version<2.5
from functools import wraps

def do_forall(attr):
def deco(func):
f_name = func.__name__
@wraps(func) # comment out if version<2.5
def wrapper(self,*args,**kwds):
for b in getattr(self,attr):
getattr(b,f_name)(*args,**kwds)
return wrapper
return deco

#====== Example =================================

class Foo(object):
def flush(self): print "Foo.flush"
def draw(self, x, y): print "Foo.draw(%s,%s)" % (x,y)

class Bar(object):
def flush(self): print "Bar.flush"
def draw(self, x, y): print "Bar.draw(%s,%s)" % (x,y)
forall_backends = do_forall('backends')

class MultiBackend(object):
"""Renders to multiple backends"""

def __init__(self, backends):
self.backends = backends

@forall_backends
def flush(self):
'Flush all backends'

@forall_backends
def draw(self, x, y):
'Draw point (x,y) on all backends'
m = MultiBackend([Foo(),Bar()])
m.flush()
m.draw(1,2)

#======================================

HTH,
George

Sep 30 '06 #6

P: n/a
The metaclass approach seems to be the one I was looking for. Thanks!

Bert

Sep 30 '06 #7

This discussion thread is closed

Replies have been disabled for this discussion.