Automatic methods in new-style classes

B

bertgoos

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
 
P

Peter Otten

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:

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

Scott David Daniels

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
(e-mail address removed)
 
S

Scott David Daniels

Scott said:
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
(e-mail address removed)
 
B

Ben Cartwright

Hey, I have the following code that has to send every command it
receives to a list of backends.
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
 
G

George Sakkis

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

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,007
Latest member
obedient dusk

Latest Threads

Top