Are python list comprehensions always a good programming practice?
- by dln385
To make the question clear, I'll use a specific example.
I have a list of college courses, and each course has a few fields (all of which are strings). The user gives me a string of search terms, and I return a list of courses that match all of the search terms. This can be done in a single list comprehension or a few nested for loops.
Here's the implementation. First, the Course class:
class Course:
def __init__(self, date, title, instructor, ID, description, instructorDescription, *args):
self.date = date
self.title = title
self.instructor = instructor
self.ID = ID
self.description = description
self.instructorDescription = instructorDescription
self.misc = args
Every field is a string, except misc, which is a list of strings.
Here's the search as a single list comprehension. courses is the list of courses, and query is the string of search terms, for example "history project".
def searchCourses(courses, query):
terms = query.lower().strip().split()
return tuple(course for course in courses if all(
term in course.date.lower() or
term in course.title.lower() or
term in course.instructor.lower() or
term in course.ID.lower() or
term in course.description.lower() or
term in course.instructorDescription.lower() or
any(term in item.lower() for item in course.misc)
for term in terms))
You'll notice that a complex list comprehension is difficult to read.
I implemented the same logic as nested for loops, and created this alternative:
def searchCourses2(courses, query):
terms = query.lower().strip().split()
results = []
for course in courses:
for term in terms:
if (term in course.date.lower() or
term in course.title.lower() or
term in course.instructor.lower() or
term in course.ID.lower() or
term in course.description.lower() or
term in course.instructorDescription.lower()):
break
for item in course.misc:
if term in item.lower():
break
else:
continue
break
else:
continue
results.append(course)
return tuple(results)
That logic can be hard to follow too. I have verified that both methods return the correct results.
Both methods are nearly equivalent in speed, except in some cases. I ran some tests with timeit, and found that the former is three times faster when the user searches for multiple uncommon terms, while the latter is three times faster when the user searches for multiple common terms. Still, this is not a big enough difference to make me worry.
So my question is this: which is better? Are list comprehensions always the way to go, or should complicated statements be handled with nested for loops? Or is there a better solution altogether?