views:

759

answers:

6

What does the last line mean in the following code?

import pickle, urllib                                                                                                                                                     

  handle = urllib.urlopen("http://www.pythonchallenge.com/pc/def/banner.p")
  data = pickle.load(handle)
  handle.close()

  for elt in data:
         print "".join([e[1] * e[0] for e in elt])

My attempt to the problem:

  • "".join... uses join -method to empty text
  • e[1] * e[0] multiplies two subsequent values in the sequence, e
  • I am not sure what is e
  • I am not sure, what it means, when you have something before for -loop, like: e[1] * e[0] for e in elt
+4  A: 

[e[1] * e[0] for e in elt] is a list comprehension, which evaluates to a list itself by looping through another list, in this case elt. Each element in the new list is e[1]*e[0], where e is the corresponding element in elt.

Nikhil Chelliah
e might be a tuple or dict
SilentGhost
e may be anything that implements __getitem__.
Torsten Marek
That's true, I made a bad assumption.
Nikhil Chelliah
+1  A: 

join() is a string method, that works on a separator in new string

>>> ':'.join(['ab', 'cd'])
>>> 'ab:cd'

and list comprehension is not necessary there, generator would suffice

SilentGhost
+7  A: 

Firstly, you need to put http:// in front of the URL, ie: handle = urllib.urlopen("http://www.pythonchallenge.com/pc/def/banner.p")

An expression [e for e in aList] is a list comprehension which generates a list of values.

With Python strings, the * operator is used to repeat a string. Try typing in the commands one by one into an interpreter then look at data:

>>> data[0]
[(' ', 95)]

This shows us each line of data is a tuple containing two elements.

Thus the expression e[1] * e[0] is effectively the string in e[0] repeated e[1] times.

Hence the program draws a banner.

Andy Dent
But switch the elements: e[0] = 95 and e[1] = ' '.
unbeknown
thanks for the correction - I am so used to doing the "sensible" thing of multiplying a string by an integer that I read it as being that way around, especially as that's how the data is stored!
Andy Dent
+14  A: 

Maybe best explained with an example:

print "".join([e[1] * e[0] for e in elt])

is the short form of

x = []
for e in elt:
  x.append(e[1] * e[0])
print "".join(x)

List comprehensions are simply syntactic sugar for for loops, which make an expression out of a sequence of statements.

elt can be an arbitrary object, since you load it from pickles, and e likewise. The usage suggests that is it a sequence type, but it could just be anything that implements the sequence protocol.

Torsten Marek
I think you mean "x.append(e[1] * e[0])"..
John Fouhy
@Marek: John is right.
Masi
`e` is a tuple in this case, so instead of `e[1]` `e[0]` could be used a more descriptive names: `''.join(astring * ntimes for ntimes, astring in elt)`
J.F. Sebastian
@John: Sure, it's fixed.
Torsten Marek
+1  A: 

Andy's is a great answer!

If you want to see every step of the loop (with line-breaks) try this out:

for elt in data:
    for e in elt:
        print "e[0] == %s, e[1] == %d, which gives us:  '%s'" % (e[0], e[1], ''.join(e[1] * e[0]))
Adam Bernier
It gives a long list of: e[0] == %s, e[1] == %d
Masi
@Masi: Thanks for noticing that. Maybe the line-break character ("\") was causing problems. I have removed it from the response.
Adam Bernier
+1  A: 

The question itself has already been fully answered but I'd like to add that a list comprehension also supports filtering. Your original line

print "".join([e[1] * e[0] for e in elt])

could, as an example, become

print "".join([e[1] * e[0] for e in elt if len(e) == 2])

to only operate on items in elt that have two elements.

markhellewell