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?
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.
Because of differences in how Python implements for loops and list comprehension, list comprehensions are almost always faster than for loops when performing operations.
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.
List comprehensions are faster than loops in general. It achieves to be faster by loading the entire list into the memory.
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])
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.
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])
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With