Comprehensions
List comprehensions are one of Python's best features.
List and Dict Comprehension
A list comprehension is a compact way to build a new list from an existing iterable. Say I have a list of project names and I want a list of their slugs:
project_names = ["Payments API", "Developer Portal", "Ops Console"]
slugs = [name.lower().replace(" ", "-") for name in project_names]
print(slugs)
# ['payments-api', 'developer-portal', 'ops-console']
The for loop equivalent does the same work in three lines:
slugs = []
for name in project_names:
slugs.append(name.lower().replace(" ", "-"))
The comprehension reads as "the slug for each name in project_names," which is closer to how I'd describe the result out loud.
You can also add an if filter to keep only the items you want:
tasks = [
{"title": "ship docs", "done": False},
{"title": "cut release", "done": True},
{"title": "announce launch", "done": False},
]
open_titles = [task["title"] for task in tasks if not task["done"]]
print(open_titles)
# ['ship docs', 'announce launch']
Dict comprehensions use a key: value expression:
projects = ["Payments API", "Developer Portal", "Ops Console"]
slug_lookup = {name: name.lower().replace(" ", "-") for name in projects}
print(slug_lookup)
# {'Payments API': 'payments-api', 'Developer Portal': 'developer-portal', 'Ops Console': 'ops-console'}
Set comprehensions use curly braces with a single expression and give you back a set with no duplicates:
raw_priorities = ["High", "high", "MEDIUM", "low", "Medium"]
unique_priorities = {p.lower() for p in raw_priorities}
print(unique_priorities)
# {'high', 'medium', 'low'}
A note on Generator comprehensions
Swap the square brackets for parentheses and you get a generator instead of a list:
task_count = sum(1 for task in tasks if not task["done"])
A generator doesn't build the full list in memory. It produces one value at a time as the when the value is requested. It's done when the values run out.
all_done = all(task["done"] for task in tasks)
If you need to iterate twice or index into the result, use a list comprehension. Generators give up random access in exchange for not allocating the whole sequence up front.
A more complex example
normalized = [
{
"title": task["title"].strip().title(),
"priority": task["priority"].strip().lower(),
"done": bool(task["done"]),
}
for task in raw_tasks
]
Tip
Use a comprehension when:
- you're looking to transform data
- the logic is short
- the result is easy to read
If the transformation starts getting hard to read, a normal for loop is often the
better choice.
zip()
zip() is handy when you want to walk two collections together.
It produces an iterable of tuples, where each value from the two collections is paired up in order.
You can pass that into the dict constructor to. make a dictionary.
field_names = ["name", "slug", "archived"]
field_values = ["Payments API", "payments-api", False]
zipped = zip(field_names, field_values)
project = dict(zipped)
That produces:
{
"name": "Payments API",
"slug": "payments-api",
"archived": False,
}