How to print side by side in Python
Want to print side by side in Python? Learn different methods, get tips and tricks, see real-world examples, and debug common errors.

You can print text side by side in Python to format output for better readability and comparison. This is a common task for displaying tables, logs, or aligned data structures.
In this article, we'll explore several techniques to align your output. We'll cover practical tips, real-world applications, and debugging advice to help you master this skill.
Using the + operator to concatenate strings
text1 = "Hello"
text2 = "World"
print(text1 + " " + text2)--OUTPUT--Hello World
The + operator is Python's most direct method for string concatenation. The example joins text1 and text2, but the key to printing them side by side is the inclusion of a literal space string: " ". This manually creates the separation needed for readability.
Without this explicit space, the strings would merge into "HelloWorld". While effective for simple joins, this technique offers little control for aligning multiple columns or handling complex layouts, which is where more advanced formatting methods shine.
Basic side-by-side printing techniques
To gain more control than the + operator offers, you can use Python's more advanced formatting options to precisely align text and structure your output.
Controlling output flow with the end parameter
print("First value:", end=" ")
print("Second value")
print("Third:", end=" ")
print("Fourth")--OUTPUT--First value: Second value
Third: Fourth
The print() function normally ends its output with a newline character, automatically moving the cursor to the next line. The end parameter lets you override this default behavior.
- By setting
end=" ", you instruct Python to add a space instead of a line break. - This causes the next
print()call to continue on the same line, placing the outputs side by side.
It’s a straightforward way to build a single line of text from several separate print statements, giving you more control over your output's layout.
Aligning text with f-strings and format specifiers
name = "Alice"
age = 30
print(f"Name: {name:<10} Age: {age}")
print(f"City: {'New York':<10} Country: {'USA'}")--OUTPUT--Name: Alice Age: 30
City: New York Country: USA
F-strings, or formatted string literals, offer a powerful way to embed expressions inside string literals. They give you fine-grained control over alignment using format specifiers.
- The syntax
{variable:<width}reserves a specific number of characters for your text. - In the example,
{name:<10}left-aligns the variablenamewithin a 10-character space. The<symbol dictates the alignment. - This ensures that subsequent text, like
Age: 30, starts at the same position, creating clean, table-like columns. It's a modern and readable approach to formatting.
Creating columns with the .format() method
item1 = "Apple"
item2 = "Banana"
price1 = 1.20
price2 = 0.80
print("{:<10} ${:.2f} {:<10} ${:.2f}".format(item1, price1, item2, price2))--OUTPUT--Apple $1.20 Banana $0.80
The .format() method offers another way to create structured layouts, functioning similarly to f-strings. It works by inserting variables into placeholders within a string template. The variables you pass to .format() fill the {} placeholders in the order they appear.
- The specifier
{:<10}pads the text to create aligned columns, just as you saw with f-strings. {:.2f}specifically formats the price, ensuring it always displays as a float with two decimal places.
While f-strings are often preferred for their readability, .format() remains a powerful and common alternative for precise text alignment.
Advanced side-by-side printing techniques
To handle more demanding scenarios, you can move beyond string formatting and use specialized tools for direct terminal output or advanced table creation.
Direct terminal output using sys.stdout.write()
import sys
sys.stdout.write("First column\t")
sys.stdout.write("Second column\n")
sys.stdout.write("Data 1\t\t")
sys.stdout.write("Data 2\n")--OUTPUT--First column Second column
Data 1 Data 2
The sys.stdout.write() function offers a more direct way to print to the terminal than the standard print() function. It writes strings directly to the output stream without adding any automatic formatting, like spaces or newlines.
- This method gives you complete control—you must manually add tab characters (
\t) for spacing and newline characters (\n) to end a line. - Because it doesn't append a newline by default, consecutive
write()calls will continue on the same line.
This makes it a powerful tool for scripts that require precise output control, though it's less convenient than f-strings for complex table layouts.
Creating tables with the tabulate library
from tabulate import tabulate
data = [["Name", "Age"], ["Alice", 30], ["Bob", 25]]
print(tabulate(data, tablefmt="plain"))--OUTPUT--Name Age
Alice 30
Bob 25
When you need more than simple alignment, the external tabulate library is your best bet for creating well-formatted tables. It simplifies the process by taking your data, structured as a list of lists, and handling all the spacing and alignment automatically.
- Each inner list you provide becomes a row in the final table.
- The
tabulate()function does the heavy lifting. You can customize the look with thetablefmtargument, where"plain"produces a clean, borderless output.
Precise screen positioning with the curses module
import curses
def main(stdscr):
stdscr.clear()
stdscr.addstr(0, 0, "Left side")
stdscr.addstr(0, 20, "Right side")
stdscr.refresh()
stdscr.getch()
curses.wrapper(main)--OUTPUT--Left side Right side
The curses module gives you total control over the terminal window, letting you build text-based user interfaces by placing text at exact coordinates. It’s a powerful tool when you need more than just sequential printing and is best suited for complex, full-screen applications.
- The entire process is managed by
curses.wrapper(), which safely initializes the screen and restores your terminal settings afterward. - The core of its power is the
addstr(y, x, "text")function. It lets you write a string at a specific row (y) and column (x), giving you precise control over your layout. - Changes are only displayed after you call
refresh(), which updates the screen with your content.
Move faster with Replit
Replit is an AI-powered development platform that transforms natural language into working applications. Describe what you want to build, and Replit Agent creates it—complete with databases, APIs, and deployment.
For the side-by-side printing techniques we've explored, Replit Agent can turn them into production tools:
- Build a command-line dashboard that displays system metrics like CPU and memory usage in neatly aligned columns.
- Create a data comparison tool that presents two datasets side by side for easy analysis.
- Deploy a currency converter that outputs a formatted table of multiple exchange rates from a single input value.
Start building by describing your application idea. Replit Agent will write the code, run tests, and deploy it for you, all from your browser.
Common errors and challenges
Even with the right tools, you can run into issues like type errors or broken layouts when aligning text side by side.
Fixing type errors when concatenating with the + operator
A common mistake is trying to join a string with a non-string type—like a number—using the + operator. This will immediately raise a TypeError because Python doesn’t automatically convert types during concatenation.
To solve this, you must explicitly convert all non-string values to strings using the str() function. Wrapping your variables, such as str(my_number), ensures that all pieces are compatible before you join them.
Troubleshooting uneven column alignment with f-strings
If your columns aren't lining up when using f-strings, it's likely because the width you've set is too small for your content. When a string is longer than the reserved space, it overflows and pushes the rest of the line's content, breaking the alignment.
The fix is to ensure your width specifier is large enough to accommodate the longest possible string for that column. It’s often helpful to determine the maximum length of your data first and set the width accordingly.
Handling \n newlines in side-by-side content
Sometimes your text alignment can be disrupted by invisible newline characters (\n) within your strings. A newline character will force a line break, splitting your content across multiple lines regardless of your formatting efforts.
You can prevent this by sanitizing your strings before printing them. Use the string’s .replace() method to remove or substitute any newline characters, for example, my_string.replace('\n', ' '), to keep your output on a single, clean line.
Fixing type errors when concatenating with the + operator
Using the + operator to join strings with other data types, like integers, will cause a TypeError. Python requires all elements to be strings for this kind of concatenation. See what happens when you try to print a name and an age together.
name = "Alex"
age = 25
print("Name: " + name + ", Age: " + age) # TypeError
The code fails because the + operator attempts to add the integer variable age directly to a string. Python can't combine these different types. See how to resolve this in the corrected version below.
name = "Alex"
age = 25
print(f"Name: {name}, Age: {age}")
The corrected code avoids the TypeError by using an f-string. This approach is cleaner and more robust for a few reasons:
- F-strings automatically convert non-string types, so you don't need to manually call
str(age). - It embeds variables directly within the string using curly braces
{}, making the code more readable.
Using f-strings is a modern best practice that helps prevent type-related concatenation errors from the start.
Troubleshooting uneven column alignment with f-strings
When using f-strings for alignment, your columns can get messy if the text is longer than the space you've reserved. The formatting breaks because the oversized content pushes everything else out of place, ruining your neat table-like structure. See what happens when one item in a product list is much longer than the specified width.
products = [("Shirt", 19.99), ("Extra Long Jacket", 59.99), ("Hat", 9.99)]
for product, price in products:
print(f"{product:<10} ${price:.2f}")
The format specifier {product:<10} reserves 10 characters, but the string "Extra Long Jacket" is longer. This overflow breaks the column alignment for that specific row. See how to resolve this in the corrected code below.
products = [("Shirt", 19.99), ("Extra Long Jacket", 59.99), ("Hat", 9.99)]
max_length = max(len(product) for product, _ in products)
for product, price in products:
print(f"{product:<{max_length}} ${price:.2f}")
The corrected code fixes alignment by dynamically calculating the column width. It first finds the length of the longest product name using max(). This value is then used as the width inside the f-string, ensuring the column is always wide enough to prevent overflow and keep your layout intact.
This technique is essential when you're formatting data of varying lengths, like names from a database, where you can't predict the longest entry beforehand.
Handling \n newlines in side-by-side content
Even with perfect alignment settings, hidden newline characters (\n) in your strings can disrupt your layout. These characters force a line break, splitting your content unexpectedly and breaking the side-by-side format. See what happens when one string contains a newline.
desc1 = "This is a\nmultiline description"
desc2 = "Single line description"
print(f"{desc1:<30} | {desc2}")
The f-string attempts to align the text, but the \n character inside desc1 forces an immediate line break. This splits the output into two lines, breaking the intended side-by-side layout. See the corrected version below.
desc1 = "This is a\nmultiline description"
desc2 = "Single line description"
lines = desc1.split("\n")
print(f"{lines[0]:<30} | {desc2}")
print(f"{lines[1]:<30} |")
The corrected code splits the string at the newline character using .split('\n'). This creates a list of lines, allowing you to print each one individually while maintaining the column structure.
The first line prints alongside the other content, and the next line prints below it, preserving the alignment. This technique is essential when working with data from files or user input, where hidden newlines can otherwise break your carefully formatted layouts.
Real-world applications
Now that you can troubleshoot common formatting issues, you can build practical tools for comparing data and visualizing results side by side.
Creating a test results comparison with f-strings
You can use f-strings to create a clear, side-by-side report that compares expected outcomes with actual results, making it easy to spot discrepancies in your tests.
expected = [10, 20, 30]
actual = [10, 22, 29]
print(f"{'Test Case':<10}{'Expected':<10}{'Actual':<10}{'Result':<10}")
for i, (exp, act) in enumerate(zip(expected, actual), 1):
result = "PASS" if exp == act else "FAIL"
print(f"{i:<10}{exp:<10}{act:<10}{result:<10}")
This script compares two lists, expected and actual, to generate a formatted test report. It uses the zip() function to pair corresponding elements from each list, creating a sequence of test data to check.
- The
enumerate()function adds a counter to each pair, which serves as the test case number. - A ternary operator (
"PASS" if exp == act else "FAIL") quickly determines the outcome for each comparison. - Each line, including the header, is printed using an f-string with alignment specifiers to create clean, side-by-side columns.
Visualizing stock price data side-by-side
You can take data comparison a step further by creating simple text-based bar charts to visualize trends, like daily stock prices, side by side.
stocks = {
"AAPL": [150.25, 152.30, 151.75, 153.80, 155.90],
"MSFT": [240.10, 242.50, 245.30, 243.75, 247.80]
}
print(f"{'Day':<8}{'AAPL':<12}{'AAPL Chart':<20}{'MSFT':<12}{'MSFT Chart':<20}")
print("-" * 70)
for day, (aapl, msft) in enumerate(zip(stocks["AAPL"], stocks["MSFT"]), 1):
aapl_bar = "█" * int(aapl / 10)
msft_bar = "█" * int(msft / 10)
print(f"{day:<8}{aapl:<12}{aapl_bar:<20}{msft:<12}{msft_bar:<20}")
This script visualizes stock data from a dictionary by creating a simple text-based bar chart. It iterates through daily prices for both stocks, which are paired together using the zip() function.
- The length of each bar is calculated by dividing the stock price by a scaling factor—in this case, 10—and repeating the
"█"character that many times. - This creates a simple visual representation of the stock's value for that day.
An f-string then prints each day's data, aligning the prices and their corresponding bars in columns for a clear side-by-side comparison.
Get started with Replit
Turn these techniques into a real tool. Describe what you want to build, like “a script that compares two CSV files and prints differing rows side-by-side” or “a currency converter that outputs a formatted table of exchange rates.”
Replit Agent will write the code, test for errors, and deploy your application directly from your browser. Start building with Replit.
Create and deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.
Create & deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.

.png)
.png)
.png)