dyao
dyao

Reputation: 1011

Translating a list comprehension into a for loop

Let's say I have this list and following code which formats the list into a nice readable format:

a = ['erin', 'david', 'chris', 'adam']

print('\n'.join('{}: {}'.format(i,j) for i,j in reversed(list(enumerate(a,1)))))

and the output is this:

4: adam
3: chris
2: david
1: erin

If I were to convert the above code into a for loop, I found that in order to get the same result as above, you have to delete the .join() method.

The for loop would look like this:

for i, j in reversed(list(enumerate(a,1))):
    print ('{}: {}'.format(i,j))   

The output:

 4: adam
 3: chris
 2: david
 1: erin

To me, the for loop makes more sense, I just dont understand why in the list comprehension, I would need the join() method to get the same result.

Upvotes: 1

Views: 582

Answers (5)

Eric Appelt
Eric Appelt

Reputation: 2973

Technically, you are using a generator expression, not a list comprehension.

The real important question (in my mind) is why do you need a join in the first place, and the reason is that print does not iterate over an iterable passed as an argument, it just calls the __str__ method to get a string representation of the object, and prints that. For a list, the __str__ method produces a representation of the list that shows its elements, so it looks as if print iterated over the list, when it actually didn't.

If you write out your generator expression explicitly:

>>> g = ('{}: {}'.format(i,j) for i,j in reversed(list(enumerate(a,1))))

and then try to print it, you will just see information about the object:

>>> print(g)
<generator object <genexpr> at 0x10fb929e8>

If you iterate over it you get a string for each element of your list a:

>>> next(g)
'4: adam'
>>> next(g)
'3: chris'
>>> next(g)
'2: david'
>>> next(g)
'1: erin'

The '\n'.join string method does iterate over the argument you pass to it, and concatenates each result into a string:

>>> g = ('{}: {}'.format(i,j) for i,j in reversed(list(enumerate(a,1))))
>>> '\n'.join(g)
'4: adam\n3: chris\n2: david\n1: erin'

When you use a for loop, you also iterate over the results of the generator expression, and print each one.

Upvotes: 2

dlask
dlask

Reputation: 8982

Without the join you only have the generator expression:

>>> ('{}: {}'.format(i,j) for i,j in reversed(list(enumerate(a,1))))
<generator object <genexpr> at 0xb72736e4>

You can collect individual generated items in the list:

>>> list('{}: {}'.format(i,j) for i,j in reversed(list(enumerate(a,1))))
['4: adam', '3: chris', '2: david', '1: erin']

but this is not the required output yet.

You can use join to create a single string:

>>> '\n'.join('{}: {}'.format(i,j) for i,j in reversed(list(enumerate(a,1))))
'4: adam\n3: chris\n2: david\n1: erin'

which, when printed, gives the required output:

>>> print('\n'.join('{}: {}'.format(i,j) for i,j in reversed(list(enumerate(a,1)))))
4: adam
3: chris
2: david
1: erin

Anyway, your "counterexample" with the old style for loop is perhaps more appropriate in this specific case, depending on the surrounding code.

Upvotes: 1

Samuel O&#39;Malley
Samuel O&#39;Malley

Reputation: 3541

Your list comprehension is closer to an equivelant of this:

string_list = []
for i, j in reversed(list(enumerate(a,1))):
    string_list.append('{}: {}'.format(i,j))   

print('\n'.join(string_list))

I know you have used generator expressions instead of actual list comprehensions, but the point is you are creating a "list" of strings and then joining them into one big string separated by newlines and printing that out. However in your second example you are looping through each element, creating the string, and printing it out (with an implicit new-line character) in each iteration.

For an easier example, if you wanted to print out the names (in your list a) you could do it in these two ways:

a = ['erin', 'david', 'chris', 'adam']
# "\n".join(a) => "erin\ndavid\nchris\nadam"
print("\n".join(a))

or:

a = ['erin', 'david', 'chris', 'adam']
for name in a:
    print(name) # prints on a new line

Upvotes: 1

Nir Friedman
Nir Friedman

Reputation: 17704

List comprehensions are generally used when you want to create an output list using an iterator. For loops are used when you want to do something with an iterator, or sometimes when the logic is a bit too complicated to stuff into a list comprehension.

In your case, the reason you need the join function is because in the for loop, you are calling print once for each string. print adds a newline each time it prints a string. With the list comprehension, you build up the entire string to be printed, and then call print once. Since you only call print once, you need to add the newlines yourself, which is what the join does.

Upvotes: 1

dpmcmlxxvi
dpmcmlxxvi

Reputation: 1302

The join is simply putting a newline character \n between the elements that your joining and then your print is writing that one long string

"4: adam\n3: chris\n2: david\n1: erin"

In the second approach the print statement is handling adding the newline character.

Upvotes: 1

Related Questions