S
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
<http://mail.python.org/pipermail/python-list/2004-July/229478.html>, 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
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
<http://mail.python.org/pipermail/python-list/2004-July/229478.html>, 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