Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

List comprehension and len() vs. simple for loop

I'm supposed to take a list of words and count all words in it which are 2 or more characters long and where the first and last character are equal.

I came up with two possible solutions:

result = 0
for word in words:
    if len(word) >= 2 and word[0] == word[-1]:
        result += 1
return result

vs.

return len([word for word in words if len(word) >= 2 and word[0] == word[-1]])

Which one would be the preferred solution? Or are there even better ones?

like image 672
helpermethod Avatar asked Nov 02 '10 23:11

helpermethod


People also ask

Is list comprehension better than for loop?

List comprehensions are the right tool to create lists — it is nevertheless better to use list(range()). For loops are the right tool to perform computations or run functions. In any case, avoid using for loops and list comprehensions altogether: use array computations instead.

Is list comprehension always faster than for loop?

Because of differences in how Python implements for loops and list comprehension, list comprehensions are almost always faster than for loops when performing operations.

What is the difference between for loop and list comprehension in Python?

The main difference between for-loop and comprehension is about performance and speed. The speed of list comprehension is notably better than for-loop because it doesn't need to load the append attribute of the list and call it as a function.

Why might you use a list comprehension instead of a loop in Python?

List comprehensions are faster than loops in general. It achieves to be faster by loading the entire list into the memory.


5 Answers

In your second example a generator expression would be better than list-comp if your list is large.

sum(1 for word in words if len(word) >= 2 and word[0] == word[-1])
like image 158
mechanical_meat Avatar answered Oct 04 '22 18:10

mechanical_meat


The first one would definitely be the preferred solution in Python.

Don't forget your Zen of Python:

The Zen of Python, by Tim Peters

Beautiful is better than ugly.

Explicit is better than implicit.

Simple is better than complex.

Complex is better than complicated.

Flat is better than nested.

Sparse is better than dense.

Readability counts.

Special cases aren't special enough to break the rules.

Although practicality beats purity.

Errors should never pass silently.

Unless explicitly silenced.

In the face of ambiguity, refuse the temptation to guess.

There should be one-- and preferably only one --obvious way to do it.

Although that way may not be obvious at first unless you're Dutch.

Now is better than never.

Although never is often better than right now.

If the implementation is hard to explain, it's a bad idea.

If the implementation is easy to explain, it may be a good idea.

Namespaces are one honking great idea -- let's do more of those!

Other than that your solutions are good.

like image 22
jacrough Avatar answered Oct 04 '22 16:10

jacrough


I personally find the explicit loop more readable, but it's much a matter of taste (some prefer shorter code generally, especially when they have to write it).

Either version can be further shortened/improved:

result = 0
for word in words:
    result += int(len(word) >= 2 and word[0] == word[-1])
return result

The int() conversions is strictly speaking unnecessary, since True is a kind of 1, but it may be better for readability. The same approach can apply to the comprehension:

return sum(len(word) >= 2 and word[0] == word[-1] for word in words)

If you want to use len(), I'd point the reader to the fact that the values don't really matter:

len(1 for word in words if len(word) >= 2 and word[0] == word[-1])
like image 45
Martin v. Löwis Avatar answered Oct 04 '22 17:10

Martin v. Löwis


Both are pretty good.

There are small differences:

List comprehension returns another list which you are passing to len. The first solution avoids creation of another list.

like image 42
pyfunc Avatar answered Oct 04 '22 18:10

pyfunc


Some other variants you might want to consider:

First, you can break the filter condition into a function. This condition is fine either way, but if it becomes any more complex I'd definitely do this:

def check(word):
    return len(word) >= 2 and word[0] == word[-1]
sum(1 for word in words if check(word))

Next, if generating a list (as in the original list comprehension) is acceptable, then you can do this:

len(filter(check, words))

There's itertools.ifilter, but if you use that you need to use the sum expression again, so it doesn't end up any clearer.

The sum trick comes up so often that I'm surprised there isn't a standard library call to count the number of items in an iterator (if there is, I havn't found it). Alternatively, it'd make sense if len would consume and count the number of entries in an iterator if it has no __len__, but it doesn't.

like image 41
Glenn Maynard Avatar answered Oct 04 '22 16:10

Glenn Maynard