The * operator unpacks arguments in a function invocation statement.
Consider this
def add(x, y):
return x + y
if you have a list t = [1,2], you can either say add(t[0], t[1]) which is needlessly verbose or you can "unpack" t into separate arguments using the * operator like so add(*t).
This is what's going on in your example.
zip(p) is like running zip([[1,2,3],[4,5,6]]). Zip has a single argument here so it trivially just returns it as a tuple.
zip(*p) is like running zip([1,2,3], [4,5,6]). This is similar to running zip(p[0], p[1]) and you get the expected output.