Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #58875 > unrolled thread

my favorite line of py code so far

Started byPeter Cacioppi <peter.cacioppi@gmail.com>
First post2013-11-08 14:22 -0800
Last post2013-11-10 16:00 -0800
Articles 15 — 5 participants

Back to article view | Back to comp.lang.python


Contents

  my favorite line of py code so far Peter Cacioppi <peter.cacioppi@gmail.com> - 2013-11-08 14:22 -0800
    Re: my favorite line of py code so far Chris Angelico <rosuav@gmail.com> - 2013-11-09 09:32 +1100
    Re: my favorite line of py code so far Peter Cacioppi <peter.cacioppi@gmail.com> - 2013-11-08 14:43 -0800
    Re: my favorite line of py code so far Peter Otten <__peter__@web.de> - 2013-11-09 08:12 +0100
    Re: my favorite line of py code so far Peter Cacioppi <peter.cacioppi@gmail.com> - 2013-11-09 01:23 -0800
      Re: my favorite line of py code so far Chris Angelico <rosuav@gmail.com> - 2013-11-09 20:28 +1100
      Re: my favorite line of py code so far Paul Rubin <no.email@nospam.invalid> - 2013-11-09 01:49 -0800
      Re: my favorite line of py code so far Peter Otten <__peter__@web.de> - 2013-11-09 12:49 +0100
      Re: my favorite line of py code so far Stefan Behnel <stefan_ml@behnel.de> - 2013-11-09 13:41 +0100
      Re: my favorite line of py code so far Chris Angelico <rosuav@gmail.com> - 2013-11-09 23:45 +1100
      Re: my favorite line of py code so far Peter Otten <__peter__@web.de> - 2013-11-09 14:49 +0100
    Re: my favorite line of py code so far Peter Cacioppi <peter.cacioppi@gmail.com> - 2013-11-10 00:57 -0800
      Re: my favorite line of py code so far Chris Angelico <rosuav@gmail.com> - 2013-11-10 21:23 +1100
    Re: my favorite line of py code so far Peter Cacioppi <peter.cacioppi@gmail.com> - 2013-11-10 00:59 -0800
    Re: my favorite line of py code so far Peter Cacioppi <peter.cacioppi@gmail.com> - 2013-11-10 16:00 -0800

#58875 — my favorite line of py code so far

FromPeter Cacioppi <peter.cacioppi@gmail.com>
Date2013-11-08 14:22 -0800
Subjectmy favorite line of py code so far
Message-ID<fbbabb45-f0e3-4e7f-968f-9822607c450a@googlegroups.com>
my fav so far is this

_ = lambda c : lambda x : c(*x)

c can be any calleable and x any iterable, but I tend to use it with a class, and then map _(class) over the result of a zip.

It must be in the library somewhere, but I haven't found it. I'm never sure what to call it, so I just reroll it into _ as needed. It's pretty easy for me to remember, but I'll probably tattoo it on my chest just in case.





[toc] | [next] | [standalone]


#58877

FromChris Angelico <rosuav@gmail.com>
Date2013-11-09 09:32 +1100
Message-ID<mailman.2265.1383949984.18130.python-list@python.org>
In reply to#58875
On Sat, Nov 9, 2013 at 9:22 AM, Peter Cacioppi <peter.cacioppi@gmail.com> wrote:
> my fav so far is this
>
> _ = lambda c : lambda x : c(*x)
>
> c can be any calleable and x any iterable, but I tend to use it with a class, and then map _(class) over the result of a zip.
>
> It must be in the library somewhere, but I haven't found it. I'm never sure what to call it, so I just reroll it into _ as needed. It's pretty easy for me to remember, but I'll probably tattoo it on my chest just in case.

So... for any given class, it returns a tweaked version that unpacks
an iterable of its arguments instead of taking separate args.
Interesting, perhaps, but not something that'll be needed in the
stdlib. But as a demo of how easily Python primitives can be combined
into some fairly elegant code, I think this is as good as any!

ChrisA

[toc] | [prev] | [next] | [standalone]


#58878

FromPeter Cacioppi <peter.cacioppi@gmail.com>
Date2013-11-08 14:43 -0800
Message-ID<022fa92a-a502-4af4-9928-8f15836e0881@googlegroups.com>
In reply to#58875
Chris said:

"So... for any given class, it returns a tweaked version that unpacks 
an iterable of its arguments instead of taking separate args. "

It works with any calleable (not just any class), but otherwise your summary is spot on.

"Interesting, perhaps, but not something that'll be needed in the stdlib."

Fair enough. In the interest of self promotion, I'm calling it the Cacioppi combinator. The tattoo apt is for Tues.

[toc] | [prev] | [next] | [standalone]


#58906

FromPeter Otten <__peter__@web.de>
Date2013-11-09 08:12 +0100
Message-ID<mailman.2279.1383981142.18130.python-list@python.org>
In reply to#58875
Peter Cacioppi wrote:

> my fav so far is this
> 
> _ = lambda c : lambda x : c(*x)
> 
> c can be any calleable and x any iterable, but I tend to use it with a
> class, and then map _(class) over the result of a zip.
> 
> It must be in the library somewhere, but I haven't found it. I'm never
> sure what to call it, so I just reroll it into _ as needed. It's pretty
> easy for me to remember, but I'll probably tattoo it on my chest just in
> case.

You mean

>>> class P:
...     def __init__(self, x, y):
...             self.x = x
...             self.y = y
...     def __repr__(self):
...             return "Point(x={0.x}, y={0.y})".format(self)
... 
>>> P(1, 2)
Point(x=1, y=2)
>>> _ = lambda c: lambda x: c(*x)
>>> list(map(_(P), zip([1,2,3], [6, 5, 4])))
[Point(x=1, y=6), Point(x=2, y=5), Point(x=3, y=4)]

? While the obvious approach would be

>>> [P(*args) for args in zip([1,2,3], [6, 5, 4])]
[Point(x=1, y=6), Point(x=2, y=5), Point(x=3, y=4)]

there is also itertools.starmap():

>>> from itertools import starmap
>>> list(starmap(P, zip([1,2,3], [6, 5, 4])))
[Point(x=1, y=6), Point(x=2, y=5), Point(x=3, y=4)]

[toc] | [prev] | [next] | [standalone]


#58921

FromPeter Cacioppi <peter.cacioppi@gmail.com>
Date2013-11-09 01:23 -0800
Message-ID<729e093a-6312-44dc-8ed4-1dd5ea344066@googlegroups.com>
In reply to#58875
Peter Otten said:


">>> _ = lambda c: lambda x: c(*x) 
>>> list(map(_(P), zip([1,2,3], [6, 5, 4]))) 
[Point(x=1, y=6), Point(x=2, y=5), Point(x=3, y=4)] 

? While the obvious approach would be 

>>> [P(*args) for args in zip([1,2,3], [6, 5, 4])] 
[Point(x=1, y=6), Point(x=2, y=5), Point(x=3, y=4)] "

I would have coded 
>>> map(_(P), zip([1,2,3], [6, 5, 4]))

Which is very concise and (to me) quite clear. Forgive me for having a bias for fewer characters.

Are you saying it's always preferable to avoid map? Is map going to be deprecated? 

I sometimes use map, sometimes comprehensions. I suspect other people do the same, that's why the language supports map and comprehensions. 

"there is also itertools.starmap(): "

Thanks, that's a bit closer to what I am doing.  To me the pure combinator is more appealing than starmap, but the presence of starmap explains why the library wouldn't need the combinator.

[toc] | [prev] | [next] | [standalone]


#58922

FromChris Angelico <rosuav@gmail.com>
Date2013-11-09 20:28 +1100
Message-ID<mailman.2286.1383989316.18130.python-list@python.org>
In reply to#58921
On Sat, Nov 9, 2013 at 8:23 PM, Peter Cacioppi <peter.cacioppi@gmail.com> wrote:
> I sometimes use map, sometimes comprehensions. I suspect other people do the same, that's why the language supports map and comprehensions.

I think map is fine if you can use a named function, but if you can't
come up with a descriptive name for what you're doing, a comprehension
is probably better (as it'll have the code right there). Mapping _
across everything tells you nothing about what it's actually doing.

ChrisA

[toc] | [prev] | [next] | [standalone]


#58923

FromPaul Rubin <no.email@nospam.invalid>
Date2013-11-09 01:49 -0800
Message-ID<7x61s1aorb.fsf@ruckus.brouhaha.com>
In reply to#58921
Peter Cacioppi <peter.cacioppi@gmail.com> writes:
>>>> [P(*args) for args in zip([1,2,3], [6, 5, 4])] 
[P(x,y) for x,y in zip(...)]

> Are you saying it's always preferable to avoid map?

Not always.  Depends on context, partly subjective.

> I sometimes use map, sometimes comprehensions. I suspect other people
> do the same, that's why the language supports map and comprehensions.

Comprehensions came along later and there was some noise made about
eliminating map for a while, though I think that's died down.

> "there is also itertools.starmap(): "
> Thanks, that's a bit closer to what I am doing.  To me the pure
> combinator is more appealing than starmap, 

You could look at the functools module too, particularly
functools.partial.  For that matter, you could also try Haskell.  It's
even more concise, and this flavor of code is easier to keep reliable
when there's compile-time type checking.

[toc] | [prev] | [next] | [standalone]


#58926

FromPeter Otten <__peter__@web.de>
Date2013-11-09 12:49 +0100
Message-ID<mailman.2288.1383997830.18130.python-list@python.org>
In reply to#58921
Peter Cacioppi wrote:

> Peter Otten said:
> 
> 
> ">>> _ = lambda c: lambda x: c(*x)
>>>> list(map(_(P), zip([1,2,3], [6, 5, 4])))
> [Point(x=1, y=6), Point(x=2, y=5), Point(x=3, y=4)]
> 
> ? While the obvious approach would be
> 
>>>> [P(*args) for args in zip([1,2,3], [6, 5, 4])]
> [Point(x=1, y=6), Point(x=2, y=5), Point(x=3, y=4)] "
> 
> I would have coded
>>>> map(_(P), zip([1,2,3], [6, 5, 4]))
> 
> Which is very concise and (to me) quite clear. Forgive me for having a
> bias for fewer characters.

Consider developing a bias for self-explanatory names ;)

> Are you saying it's always preferable to avoid map? 

No. I'm saying that if you have a problem with multiple solutions you should 
pick the boringly obvious one if you plan to reuse or publish the code.

There is no obvious meaning attached to _ -- so don't use it.

> Is map going to be deprecated?

Not to my knowledge. I use it when I already have a function ready 
to be called with the items in a sequence. Continuing the Point example that 
could be

map(Point, xvals, yvals)

or

map(Point.from_tuple, xypairs)

> I sometimes use map, sometimes comprehensions. I suspect other people do
> the same, that's why the language supports map and comprehensions.
> 
> "there is also itertools.starmap(): "
> 
> Thanks, that's a bit closer to what I am doing.  To me the pure combinator
> is more appealing than starmap, but the presence of starmap explains why
> the library wouldn't need the combinator.

I found only a single use of starmap on my machine (and one more in the 
stdlib)

# in redis.client
all_cmds = ''.join(starmap(connection.pack_command,
                           [args for args, options in commands]))

and frankly, I would prefer

all_cmds = "".join(
    connection.pack_command(*args) for args, options in commands)

which by some strange coincidence also requires fewer letters to type.

[toc] | [prev] | [next] | [standalone]


#58927

FromStefan Behnel <stefan_ml@behnel.de>
Date2013-11-09 13:41 +0100
Message-ID<mailman.2289.1384000887.18130.python-list@python.org>
In reply to#58921
Peter Otten, 09.11.2013 12:49:
> There is no obvious meaning attached to _ -- so don't use it.

Not quite true. Depending on the context, the obvious meanings of "_" in
Python are either

1) "ignore me", e.g. in

    _, b = some_tuple

or

2) "this is a non-public thing", as in

    class Xyz:
        _private = 1

or

3) "I am the translation function", as commonly used in (HTML) templates:

    _("translation key here")


None of the three meanings applies to the OP's example, AFAICT.

Stefan

[toc] | [prev] | [next] | [standalone]


#58929

FromChris Angelico <rosuav@gmail.com>
Date2013-11-09 23:45 +1100
Message-ID<mailman.2290.1384001149.18130.python-list@python.org>
In reply to#58921
On Sat, Nov 9, 2013 at 11:41 PM, Stefan Behnel <stefan_ml@behnel.de> wrote:
> 2) "this is a non-public thing", as in
>
>     class Xyz:
>         _private = 1

Your three meanings all have the etymology of "ignore me", but I would
distinguish this one from the others. An underscore used on its own
has meaning; an underscore as part of a larger token has quite
different meaning, and can have any of several - "private-ish" (as
above), "mangle me" (double leading, no trailing), "magic" (dunder -
double leading/double trailing), and "word separator"
(between_multiple_words_in_an_identifier). Your other two examples are
what we're talking about here, though.

ChrisA

[toc] | [prev] | [next] | [standalone]


#58940

FromPeter Otten <__peter__@web.de>
Date2013-11-09 14:49 +0100
Message-ID<mailman.2296.1384004974.18130.python-list@python.org>
In reply to#58921
Stefan Behnel wrote:

> Peter Otten, 09.11.2013 12:49:
>> There is no obvious meaning attached to _ -- so don't use it.
> 
> Not quite true. Depending on the context, the obvious meanings of "_" in
> Python are either
> 
> 1) "ignore me", e.g. in
> 
>     _, b = some_tuple
> 
> or
> 
> 2) "this is a non-public thing", as in
> 
>     class Xyz:
>         _private = 1
> 

This doesn't fit the bill provided we're discussing the name _ rather than 
any underscore.
> 
> 3) "I am the translation function", as commonly used in (HTML) templates:
> 
>     _("translation key here")
> 
> 
> None of the three meanings applies to the OP's example, AFAICT.

I can come up with one more, the last non-None result calculated in the 
interactive interpreter,

>>> 2*2
4
>>> _
4

so we're back to three. All seem to be established conventions rather than 
obvious choices.

[toc] | [prev] | [next] | [standalone]


#58993

FromPeter Cacioppi <peter.cacioppi@gmail.com>
Date2013-11-10 00:57 -0800
Message-ID<dae981ac-14e7-47c8-89ed-a5ad21e956ca@googlegroups.com>
In reply to#58875
Chris said :

"I think map is fine if you can use a named function, but if you can't
come up with a descriptive name for what you're doing, a comprehension
is probably better (as it'll have the code right there). Mapping _
across everything tells you nothing about what it's actually doing"

OK, this makes a lot of sense. (Any my apologies to Peter Otten if my previous post was overly snarky).

How about this? I put the following in my catchall_utility.py module

  # the oneArg combinator takes a multi-argument calleable
  # and returns the equivalent single argument calleable
  oneArg = lambda c: lambda x : c(*x)


With example code

  map(oneArg(P), zip([1,2,3], [6, 5, 4]))

To my mind the idea that oneArg(P) is a calleble that behaves exactly like P, with the only difference being oneArg(P) accepts P's arguments packed into a single iterable, is very intuitive.

I actually find the oneArg version more readable than the comprehension that unpacks explicitly using *. At this point I probably have equal experience coding Lisp and Py, with the latter more recent, so I don't think this bias can be chalked up to "experienced functional programmer, beginner py programmer".

To be clear, I was never really intending to keep the

  _ = lambda c : lambda x : c(*x)
  map(oneArg(P), zip([1,2,3], [6, 5, 4]))

code snippets in my final work product. The purpose of this thread was too fish around for ideas on what to replace it with when I execute the "use the unit tests to facilitate refactoring for better names and more professional idioms" step.

(We never, ever, skip that step, right?)

Although it is unlikely you will convince me to use the comprehension in this sort of situation, it's possible someone could convince me to use starmap with P instead of map with oneArg(P). Those two techniques are fully equivalent. But, to my mind, the latter just feels right, and I find that I write fewer bugs when I use idioms that resonate.

Thanks again, good thread for me

[toc] | [prev] | [next] | [standalone]


#58997

FromChris Angelico <rosuav@gmail.com>
Date2013-11-10 21:23 +1100
Message-ID<mailman.2328.1384079035.18130.python-list@python.org>
In reply to#58993
On Sun, Nov 10, 2013 at 7:57 PM, Peter Cacioppi
<peter.cacioppi@gmail.com> wrote:
> Chris said :
>
> "I think map is fine if you can use a named function, but if you can't
> come up with a descriptive name for what you're doing, a comprehension
> is probably better (as it'll have the code right there). Mapping _
> across everything tells you nothing about what it's actually doing"
>
> How about this? I put the following in my catchall_utility.py module
>
>   # the oneArg combinator takes a multi-argument calleable
>   # and returns the equivalent single argument calleable
>   oneArg = lambda c: lambda x : c(*x)

Now it has a name, so I could imagine you importing it into modules
and using it usefully. I'd be inclined to call it something along the
lines of "unpacked" but that's just bikeshedding.

> ... I find that I write fewer bugs when I use idioms that resonate.

Absolutely! The unfortunate truth, though, is that idioms that
resonate with you _and nobody else_ are just as big a problem as bugs,
because they're unmaintainable. So hopefully what you're doing will
make sense to other people too!

ChrisA

[toc] | [prev] | [next] | [standalone]


#58994

FromPeter Cacioppi <peter.cacioppi@gmail.com>
Date2013-11-10 00:59 -0800
Message-ID<3b7385e0-da4b-4482-ac62-904d3a624546@googlegroups.com>
In reply to#58875
Sorry, typo, meant to say

To be clear, I was never really intending to keep the

  _ = lambda c : lambda x : c(*x)
  map(_(P), zip([1,2,3], [6, 5, 4]))

code snippets in my final work product. The purpose of this thread was too fish around for ideas on what to replace it with... 
 

[toc] | [prev] | [next] | [standalone]


#59033

FromPeter Cacioppi <peter.cacioppi@gmail.com>
Date2013-11-10 16:00 -0800
Message-ID<71b09c61-85b3-4c47-8dab-c27e6fe13dbb@googlegroups.com>
In reply to#58875
Chris said :

"Absolutely! The unfortunate truth, though, is that idioms that
resonate with you _and nobody else_ are just as big a problem as bugs,
because they're unmaintainable. So hopefully what you're doing will
make sense to other people too! "

There is some truth in what you say ...  but in this case we're talking about an algorithmically trivial portion of a project for which I am basically BDFL. So as long as I'm not totally in left field, I'm going with what feels truthy to me.

Also, 

  lambda c : lambda x : c(*x)

would make a sweet tattoo. Much better than some Kanji that might as well translate into "dumb white guy" for all I know ;)
 


[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web