Re: Securing a future for anonymous functions in Python

Discussion in 'Python' started by Jp Calderone, Dec 30, 2004.

  1. Jp Calderone

    Jp Calderone Guest

    On Fri, 31 Dec 2004 00:19:29 +1000, Nick Coghlan <> wrote:
    >Jp Calderone wrote:
    > > On Fri, 31 Dec 2004 00:00:31 +1000, Nick Coghlan <> wrote:
    > >
    > >>Paul Rubin wrote:
    > >>
    > >>>Nick Coghlan <> writes:
    > >>>
    > >>>
    > >>>>Anyway, I'm looking for feedback on a def-based syntax that came up in
    > >>>>a recent c.l.p discussion:
    > >>>
    > >>>
    > >>>Looks like just an even more contorted version of lambda. It doesn't
    > >>>fix lambda's main deficiency which is inability to have several
    > >>>statements in the anonymous function.
    > >>
    > >>Do you consider generator expressions or list comprehensions deficient because
    > >>they don't allow several statements in the body of the for loop?
    > >>

    > >
    > >
    > > >>> (foo + bar

    > > ... for foo in range(13) if foo % 3 == 2
    > > ... for bar in range(16, 23) if 2 <= bar % 5 < 4)
    > > <generator object at 0xb7dec10c>
    > > >>>

    > >
    > > Hmm. Two for loops and two if clauses. That's four altogether.
    > > Does that qualify as "several"? :) Sure, they're not statements
    > > according to the grammar, but that's entirely beside the point.
    > >
    > > Jp

    >
    > And that's an expression, and hence perfectly legal as the body of an anonymous
    > function.


    Quoting my response again:

    > Jp Calderone wrote:
    > > Sure, they're not statements according to the grammar, but
    > > that's entirely beside the point.


    I thought this was obvious to anyone, oh well.

    List and generator comprehensions have special syntax so that behavior
    normally acheivable only with statements can be used to control their
    execution. Lambdas don't have any similar special syntax.

    I'm not saying "boo hoo lambdas are crippled fix them waah". I'm saying
    "Lambdas and generator comprehensions are not comparable in this sense and
    arguments based on one should not be used to support positions about the
    other".

    Jp
     
    Jp Calderone, Dec 30, 2004
    #1
    1. Advertising

  2. Jp Calderone wrote:
    > I'm not saying "boo hoo lambdas are crippled fix them waah". I'm

    saying
    > "Lambdas and generator comprehensions are not comparable in this

    sense and
    > arguments based on one should not be used to support positions about

    the
    > other".


    This post and Michael Spencer's post which proposed (expr for args(a,
    *b, **c)) got me wondering whether this could be done already today.
    My solution is unfortuantely implementation-specific. I can't find a
    way for a generator (let alone a generator expression) to refer to
    itself; neither do generators have writable attributes. With those
    features, one can imagine a more portable (and more verbose) solution.

    I find that I don't really like the generator-like syntax. To me, it
    makes sense for an unnamed function to look like a named function
    without a name, eg:

    callbacks['foo'] = def (a,b): ... expr or statement? ...

    It seems to work in Lua (not that I'm a big fan of Lua). Thus, I'm not
    sure what my point is with this code; maybe just that there are
    workarounds if lambda ceases to exist. Also, with this sort of
    solution, nobody can complain that genexps/listcomps/"lambdas" get
    preferential treatment <wink>

    Anyway, this is tested in Win32 python 2.4. It adds a function "fn"
    (name stolen from Arc) which turns a specially-written generator
    expression into an anonymous function. Default args, *args, *kwargs
    are all unsupported. I'm sorry if google groups eats my leading
    whitespace; I've one-lined things to reduce the effect.

    def fn(gen):
    """Turns a generator expression into a callable."""
    def anonymous(*args): return gen.next()
    return anonymous

    def args():
    """Works with fn(); yields args passed to anonymous()."""
    while True: yield sys._getframe(2).f_locals['args']

    args = args()

    foo = fn(a + b * c for (a,b,c) in args)
    assert foo(3,4,5) == 3+4*5
    assert foo(4,5,6) == 4+5*6
     
    Paul L. Du Bois, Dec 30, 2004
    #2
    1. Advertising

  3. Paul L. Du Bois <> wrote:
    ...
    > are all unsupported. I'm sorry if google groups eats my leading
    > whitespace; I've one-lined things to reduce the effect.


    It does/did, so let me repost while fixing it since this is truly,
    deliciously evil:

    > def fn(gen):
    > """Turns a generator expression into a callable."""
    > def anonymous(*args): return gen.next()
    > return anonymous
    >
    > def args():
    > """Works with fn(); yields args passed to anonymous()."""
    > while True: yield sys._getframe(2).f_locals['args']
    >
    > args = args()
    >
    > foo = fn(a + b * c for (a,b,c) in args)
    > assert foo(3,4,5) == 3+4*5
    > assert foo(4,5,6) == 4+5*6


    Paul, you really SHOULD have posted this BEFORE I had to send in the
    files for the 2nd ed's Coobook... this gets my vote for the most
    delightful abuse of sys._getframe even (and I've seen quite a few;-).

    Kudos!!!


    Alex
     
    Alex Martelli, Dec 31, 2004
    #3
  4. Alex Martelli wrote:
    > Paul L. Du Bois <> wrote:
    >>def fn(gen):
    >> """Turns a generator expression into a callable."""
    >> def anonymous(*args): return gen.next()
    >> return anonymous
    >>
    >>def args():
    >> """Works with fn(); yields args passed to anonymous()."""
    >> while True: yield sys._getframe(2).f_locals['args']
    >>
    >>args = args()
    >>
    >>foo = fn(a + b * c for (a,b,c) in args)
    >>assert foo(3,4,5) == 3+4*5
    >>assert foo(4,5,6) == 4+5*6

    >
    >
    > Paul, you really SHOULD have posted this BEFORE I had to send in the
    > files for the 2nd ed's Coobook... this gets my vote for the most
    > delightful abuse of sys._getframe even (and I've seen quite a few;-).


    So, I couldn't figure out why this worked until I started to write an
    email to ask. Once I understood it, I figured it wouldn't hurt to send
    my thoughts out anyway to (1) verify that I understand it right, and (2)
    help anyone else who was trying to figure this out.


    As I understand it sys._getframe(2).f_locals should get the names local
    to the stack frame two above the current one. So, in the context of:
    fn(... for ... in args)
    sys._getframe(2).f_locals should be looking at the names local to the
    'anonymous' function in the 'fn' function, because one stack frame up
    from the 'args' function is the generator's 'next' function, and two
    stack frames up is the 'anonymous' function. That means that:
    sys._getframe(2).f_locals['args']
    gets whatever object has been bound to 'args' in:
    def anonymous(*args):
    So then in:
    foo = fn(a + b * c for (a,b,c) in args)
    foo(3,4,5)
    foo(4,5,6)
    sys._getframe(2).f_locals['args'] will get (3, 4, 5) in the first foo
    call, (4, 5, 6) in the second foo call, etc.


    So basically the way a call like foo(3, 4, 5) works is:
    (1) foo(3, 4, 5) calls gen.next() where gen is the generator expression
    (2) gen.next() calls args.next()
    (3) args.next() returns the (3, 4, 5) argument tuple of foo by looking
    up the stack frames
    (4) gen.next() binds (3, 4, 5) to the names a, b, c respectively
    (5) gen.next() returns the value of "a + b * c" for these bindings
    (6) foo(3, 4, 5) returns the same value (as gen.next() did)

    Does that seem about right?

    Steve

    P.S. That's so *evilly* cool!
     
    Steven Bethard, Dec 31, 2004
    #4
  5. Steven Bethard <> wrote:

    > Does that seem about right?


    Yep!

    > P.S. That's so *evilly* cool!


    We should have an Evilly Cool Hack of the Year, and I nominate Paul du
    Bois's one as the winner for 2004. Do I hear any second...?


    Alex
     
    Alex Martelli, Dec 31, 2004
    #5
  6. Jp Calderone

    Paul Rubin Guest

    (Alex Martelli) writes:
    > We should have an Evilly Cool Hack of the Year, and I nominate Paul du
    > Bois's one as the winner for 2004. Do I hear any second...?


    The year's not over yet :).
     
    Paul Rubin, Dec 31, 2004
    #6
  7. Jp Calderone

    Paul Du Bois Guest

    Alex Martelli wrote:
    > We should have an Evilly Cool Hack of the Year, and I nominate Paul

    du
    > Bois's one as the winner for 2004. Do I hear any second...?


    Thank you :)

    I am busy writing it up as a recipe. I think I have a pleasing way for
    it to be portable, even. Unfortunately, that removes some of the evil.
    p
     
    Paul Du Bois, Dec 31, 2004
    #7
  8. Alex Martelli wrote:
    > We should have an Evilly Cool Hack of the Year, and I nominate
    > Paul Du Bois's one as the winner for 2004. Do I hear any second...?


    Oh bother, I just realized I sent my first reply using a good email
    address. Hope that cancel goes through quickly.

    Anyway, thank you! I've submitted it as a recipe. It includes a
    less-evil portable version, which might be improved further if PEP 288
    ever comes through.

    p

    The recipe:
    http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/361452
     
    Paul L. Du Bois, Dec 31, 2004
    #8
  9. Paul Rubin wrote:
    > (Alex Martelli) writes:
    >
    >>We should have an Evilly Cool Hack of the Year, and I nominate Paul du
    >>Bois's one as the winner for 2004. Do I hear any second...?

    >
    > The year's not over yet :).


    Ok, now that we're past 0:00:00 UTC, I'll second that nomination! ;)

    Steve

    P.S. Happy New Year all!
     
    Steven Bethard, Jan 1, 2005
    #9
    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. Xiangliang Meng
    Replies:
    1
    Views:
    1,615
    Victor Bazarov
    Jun 21, 2004
  2. Nick Coghlan
    Replies:
    85
    Views:
    1,261
    Jeff Shannon
    Jan 12, 2005
  3. Reporter
    Replies:
    3
    Views:
    477
    Mike Schilling
    May 12, 2007
  4. Cristian
    Replies:
    66
    Views:
    1,112
    Bruno Desthuilliers
    Sep 24, 2007
  5. Replies:
    1
    Views:
    225
Loading...

Share This Page