Curried class methods?

2006-08-16 Thread Scott Lamb
I'm trying to dynamically generate class methods which have access to
some state passed in at creation time. (Basically as a workaround to
twisted's trial not having a way to dynamically add stuff. Plain
unittest seems to have TestSuite, but the trial runner doesn't know
about it.)

My first attempt might better illustrate this -

class Foo:
def generalized(self, ctx):
print 'my ctx is %r' % ctx

for i in ['a','b','c']:
setattr(Foo, i, lambda self: self.generalized(i))

foo = Foo()
foo.a()
foo.b()
foo.c()

but this prints "my ctx is c" three times; I'd hoped for a, b, c, of
course. After reading
, I
think I understand why this is - "i" doesn't actually get added to each
new function's context; they just reference the global one. Even if I
do this:

def builder():
for i in ['a','b','c']:
setattr(Foo, i, lambda self: self.generalized(i))
builder()

they'll just keep a reference to the context that was made on entry to
builder() and share it, so the modifications builder() makes to i are
reflected in all three functions.

Okay, take two. I tried this:

try:
from functional import partial
except ImportError:
...partial pasted from PEP 309...

for i in ['a','b','c']:
setattr(Foo, i, partial(Foo.generalized, ctx=i))

but when I try to call foo.a(), I get this error:

Traceback (most recent call last):
  File "./foo.py", line 35, in ?
foo.a()
  File "./foo.py", line 25, in __call__
return self.fn(*(self.args + args), **d)
TypeError: unbound method generalized() must be called with Foo
instance as first argument (got nothing instead)

If I add a debug print to partial.__call__,

print 'partial.__call__(args=%r, kw=%r, self.kw=%r)' \
  % (args, kw, self.kw)

I see:

partial.__call__(args=(), kw={}, self.kw={'ctx': 'a'})

I'd first expected foo.a() to be equivalent to Foo.a(self), but instead
it's like Foo.a(). There must be magic that does the equivalent of

class Foo:
def __init__(self):
a = partial(a, self)

for real Python functions and not for my partial object.

With this __init__ magic, I guess I have something that works. I have
to apply the partial twice, though - if I do everything in the
__init__, my new functions won't exist by the time trial's
introspection kicks in, so they'll never get called. My ugly hack has
gotten even uglier.

Does anyone know of a better way to do this?

Thanks,
Scott

-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Curried class methods?

2006-08-17 Thread Scott Lamb
Thanks, Antoon and Carl. Just tried your solutions - both work and are
much cleaner than mine.

-- 
http://mail.python.org/mailman/listinfo/python-list