Comprehensions are a nice and expressive way to work with data structures. Let's start with a simple example:
{el**2 for el in range(3)}
>>> {0, 1, 4}
Here, the curly brackets define our result. We use range to create the initial iterable, and then loop over its values, computing the square value of each. This is not a real loop, though. List comprehensions are actually faster than loops and even map, as there are no lambdas, and thus, no additional costs for stack lookups:
>>> %%timeit
... s = set()
... for el in range(10):
... s.add(el**2)
3.35 µs ± 134 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
>>> %timeit set(map(lambda x: x**2, range(10)))
3.72 µs ± 207 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
>>> %timeit {el**2 for el in range(10)}
3.11 µs ± 309...