size = int(input("Max multiplier: "))
for row in range(1, size + 1):
line_parts = []
for col in range(1, row + 1):
line_parts.append(f"{col}×{row}={row*col}")
print(" ".join(line_parts))
The snippet above requests an integer from the user, then builds the lower-triangular multiplication grid up to that value. Each inner loop colletcs the products for the current row into a list; after the inner loop finishes, the list is joined into a single string and printed, yielding neatly aligned output such as:
1×1=1
1×2=2 2×2=4
1×3=3 2×3=6 3×3=9
...
For a right-aligned, tabular layout you can pre-compute the maximum width of any cell and use str.rjust:
size = int(input("Max multiplier: "))
max_width = len(f"{size}×{size}={size*size}")
for row in range(1, size + 1):
for col in range(1, row + 1):
print(f"{col}×{row}={row*col}".rjust(max_width), end=" ")
print()
Both versiosn rely only on built-in functions and work in any standard Python 3 interpreter.