Curried class methods?

Discussion in 'Python' started by Scott Lamb, Aug 17, 2006.

  1. Scott Lamb

    Scott Lamb Guest

    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
     
    Scott Lamb, Aug 17, 2006
    #1
    1. Advertising

  2. On 2006-08-17, Scott Lamb <> wrote:
    > 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:


    Try this instead as the for loop

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

    --
    Antoon Pardon
     
    Antoon Pardon, Aug 17, 2006
    #2
    1. Advertising

  3. Scott Lamb

    Carl Banks Guest

    Scott Lamb wrote:
    > 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.


    def build(j):
    setattr(Foo, j, lambda self: self.generalized(j))
    for i in ["a","b","c"]:
    build(i)

    Each call of the the build function creates its own cell "j" that the
    lambda references.


    Carl Banks
     
    Carl Banks, Aug 17, 2006
    #3
  4. Scott Lamb

    Scott Lamb Guest

    Thanks, Antoon and Carl. Just tried your solutions - both work and are
    much cleaner than mine.
     
    Scott Lamb, Aug 17, 2006
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. E11
    Replies:
    1
    Views:
    4,792
    Thomas Weidenfeller
    Oct 12, 2005
  2. =?ISO-8859-1?Q?Holger_T=FCrk?=

    Curried functions

    =?ISO-8859-1?Q?Holger_T=FCrk?=, Jun 4, 2004, in forum: Python
    Replies:
    0
    Views:
    280
    =?ISO-8859-1?Q?Holger_T=FCrk?=
    Jun 4, 2004
  3. Oltmans
    Replies:
    6
    Views:
    353
    Terry Reedy
    Mar 11, 2009
  4. Kenneth McDonald
    Replies:
    5
    Views:
    326
    Kenneth McDonald
    Sep 26, 2008
  5. svend
    Replies:
    6
    Views:
    143
    Dom Leonard
    Jul 25, 2003
Loading...

Share This Page