views:

195

answers:

5

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.

+4  A: 

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
+6  A: 

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
I was about to suggest the same
Albert Visser
Looks better; except I'd use `items()` instead of `iteritems()` as that is deprecated in Py3k (and `colprint` accepts a flattened list anyway)
Sridhar Ratnakumar
A: 

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())
    ))
THC4k
? What language is this? I get syntax error in Python.
Sridhar Ratnakumar
The 2nd is Python **3**
THC4k
+2  A: 

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
A: 

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