Multiplication Grid function I've made a small function to output a multiplication grid (those things from primary school! :P) to an inputted number.

def make_grid(max):
    max += 1  # so the for loops go up to the desired number, inclusive
    print('X\t' + '\t'.join([str(_) for _ in range(max)]) + '\n')  # print all the numbers hoizontally
    for i in range(max):
        print(i, end="\t")  # print the vertical column of base numbers
        for x in range(max):
            print(str(i*x), end="\t")  # multiply the two numbers
        print('\n')

if __name__ == "__main__":
    try:
        max = int(input("Max Number? "))
        make_grid(max)
    except ValueError:
        print("Please enter a number!")


Choosing a max number of 5 (make_grid(5)) would output:


X 0   1   2   3   4   5

0 0   0   0   0   0   0   

1 0   1   2   3   4   5   

2 0   2   4   6   8   10  

3 0   3   6   9   12  15  

4 0   4   8   12  16  20  

5 0   5   10  15  20  25



I'm looking for any improvements I could make, especially the \t's - as I use them to line up the numbers, but the gap gets pretty big, unnecessarily.
make_grid() isn't quite the right name for this function, as it is printing a grid rather than returning a grid.  I think you would be better off returning a long string to be printed.

In your list comprehension, _ is a bad choice for the iteration variable name, since _ indicates a throwaway value, by convention.  If you then call str(_), you should pick a different name.

Besides the excessively wide spacing as you have observed, using tabs is problematic because the output would be better with the entries right-aligned.  You also have a superfluous tab at the end of each line (except the header row).

Suggested solution

Use '{0:&gt;{width}}'.format(…) to print the entries right-aligned with a fixed width.

def multiplication_grid(max):
    max += 1    # Inclusive upper bound for ranges
    grid = [['X'] + [j for j in range(max)]] + \
             [[i] + [i * j for j in range(max)] for i in range(max)]

    # Use fixed-width right alignment for every entry in the 2D grid
    width = 2 + len(str(max * max))
    fmt = lambda n: '{0:&gt;{width}}'.format(n, width=width)
    return '\n\n'.join(''.join(fmt(entry) for entry in row) for row in grid)


[['X'] + [j for j in range(max)]] creates the header row.

[[i] + [i * j for j in range(max)] for i in range(max)] is a nested list comprehension to create the rest of the rows, where i is the row number and j is the column number.

lambda is a way to define very simple functions that do nothing more than returning an expression.  The lambda could also have been written as:

    def fmt(n):
        return '{0:&gt;{width}}'.format(n, width=width)


The last line applies fmt() to every entry in the two-dimensional grid and joins everything into a single string.