The asterisk (*) performs argument unpacking, which uses the elements of pairs as individual arguments to zip (link)
The zip function transforms multiple iterables into a single iterable of tuples of corresponding function: (link)
The random module actually produces pseudorandom (that is, deterministic) numbers based on an internal state that you can set with random.seed if you want to get reproducible results: (link)
Not infrequently, when we’re iterating over a list or a generator we’ll want not just the values but also their indices. For this common case Python provides an enumerate function, which turns values into pairs (index, value): (link)
Often all we need is to iterate over the collection using for and in. In this case we can create generators, which can be iterated over just like lists but generate their values lazily on demand. (link)
A slice can take a third argument to indicate its stride, which can be negative:
every_third = x[::3] (link)
IPython has a magic function called %paste, which correctly pastes whatever is on your clipboard, whitespace and all. This alone is a good reason to use IPython. (link)
Now that you have your environment, it’s worth installing IPython, which is a full-featured Python shell: (link)
As a matter of good discipline, you should always work in a virtual environment, and never using the “base” Python installation (link)