6

Consider the following code:

>>> colprint([
        (name, versions[name][0].summary or '')
        for name in sorted(versions.keys())
    ])

What this code does is to print the elements of the dictionary versions in ascending order of its keys, but since the value is another sorted list, only the summary of its first element (the 'max') is printed.

Since I am familiar with let from lisp, I rewrote the above as:

>>> colprint([
        (name, package.summary or '')
        for name in sorted(versions.keys())
        for package in [versions[name][0]]
    )]

Do you think this violates being Pythonic? Can it be improved?

Note: For the curious, colprint is defined here.

Community
  • 1
  • 1
Sridhar Ratnakumar
  • 81,433
  • 63
  • 146
  • 187
  • 3
    Heh. Trying to be lispy is _always_ unpythonic. Python tries not to be lispy as hard as it can. – Svante Dec 15 '09 at 22:10
  • 2
    It's tough to speak Parsel with a lisp ;[. – Tordek Dec 16 '09 at 22:39
  • python doesn't try to not be like lisp. GvR Himself has said that PYthon and Lisp are very similar, and that the differences are more superficial. I come from a scheme back ground myself, but I have been using python almost exclusively for three years now, and I like the second way more than the first. – Broseph Apr 21 '13 at 01:15

5 Answers5

7

Why not exploit tuples?

colprint([(name, version[0].summary or '')
      for (name, version) in sorted(versions.iteritems())])

or, even

colprint(sorted([(name, version[0].summary or '')
             for (name, version) in versions.iteritems()]))

Also, you may consider (in my first example) removing the []s, because that way you get a generator instead of a list (which may or may not be useful, since I'm guessing this'll print the whole array, so you won't be saving any evaluations).

Tordek
  • 10,628
  • 3
  • 36
  • 67
5

I wouldn't use the "tricky for clause" (or "let-equivalent") in most cases, but I would if it's the natural way to avoid repetition, especially costly repetition. E.g.

xs = [(y, y*1.2, y-3.4) for z in zs for y in [somefun(z)] ]

looks much better to me than calling somefun three times!-) So, it's worth keeping in mind, even if probably not worth using where it does not remove repetition.

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
4

So you're using "for x in [y]" as a substitute for "let x y".

Trying to emulate language's syntax in another language is never a good idea. I think that the original version is much clearer.

Amnon
  • 7,652
  • 2
  • 26
  • 34
1

As Tordek says, you can use items() or iteritems() in this case to avoid the issue:

colprint(sorted((name, packages[0].summary or '')
                for (name, packages) in versions.items()))

Moving the sorting outside is a nice touch.

[Note that the use of items() changed the sorting order slightly - it used to be by name with ties resolved by original order (Python sort is stable), now it's by name with ties resolved by summary. Since the original order of a dict is random, the new behaviour is probably better.]

But for other uses (such as Alex Martelli's example), a "let"-alike might still be useful. I've also once discovered the for var in [value] trick, but I now find it ugly. A cleaner alternative might be a "pipeline" of comprehensions / generators, using the "decorate/undecorate" trick to pass the added value in a tuple:

# You could write this with keys() or items() - 
# I'm just trying to examplify the pipeline technique.
names_packages = ((name, versions[name][0]) 
                  for name in versions.keys())

names_summaries = ((name, package.summary or '')
                   for (name, package) in names_packages)

colprint(sorted(names_summaries))

Or applied to Alex's example:

ys = (somefun(z) for z in zs)
xs = [(y, y*1.2, y-3.4) for y in ys]

(in which you don't even need the original z values, so the intermediate values don't have to be tuples.)

See http://www.dabeaz.com/generators/ for more powerful examples of the "pipeline" technique...

Beni Cherniavsky-Paskin
  • 9,483
  • 2
  • 50
  • 58
0

You can move the sorting to the end to avoid some intermediate lists.

This looks a bit nicer i guess:

colprint(sorted(
        (name, version[0].summary or '')
        for (name,version) in versions.iteritems())
    ))

Python3 can do even better:

colprint(sorted(
        (name, first_version.summary or '')
        for (name,(first_version,*_)) in versions.items())
    ))
u0b34a0f6ae
  • 48,117
  • 14
  • 92
  • 101
Jochen Ritzel
  • 104,512
  • 31
  • 200
  • 194