Skip to main content

How to Capture print Output to a Variable in Python (redirect stdout)

The print() function in Python sends output to the standard output stream (sys.stdout), typically the console.

This guide explains how to capture this output and store it in a variable, instead of (or in addition to) printing it to the console. This is useful for testing, logging, and manipulating output programmatically. We'll focus on the best practice using contextlib.redirect_stdout and also cover the underlying mechanism using io.StringIO.

Why Direct Assignment Doesn't Work

It's important to understand why you can't simply do this:

my_variable = print("Hello, world!")  # This DOES NOT work as intended.

The print() function, by design, always returns None. It writes to sys.stdout (usually the console), but its return value is None. Therefore, my_variable will be assigned None, not the string "Hello, world!".

The contextlib.redirect_stdout context manager provides the cleanest, safest, and most Pythonic way to capture print output:

from io import StringIO
import contextlib
import sys # Import the sys module

# Create a StringIO object to capture output
string_io = StringIO()

with contextlib.redirect_stdout(string_io):
print('This will be captured, not printed to the console.')
print('This too.')

# Get the captured output from the StringIO object
output = string_io.getvalue()

print("Captured output:") # This will be printed to stdout.
print(output)

Output:

Captured output:
This will be captured, not printed to the console.
This too.
  • io.StringIO: This class creates an in-memory text stream. It acts like a file, but the data is stored in a string buffer instead of on disk. This is much more efficient than writing to a temporary file.
  • contextlib.redirect_stdout(string_io): This is a context manager (used with the with statement). It temporarily redirects sys.stdout to the string_io object. Anything printed within the with block goes to the string_io buffer instead of the console.
  • string_io.getvalue(): After the with block, this retrieves the entire contents of the string_io buffer as a single string. The getvalue() method does not clear the buffer.
  • sys.stdout restored automatically: Importantly, redirect_stdout automatically restores sys.stdout to its original value when the with block exits, even if an exception occurs. This is much safer than manually changing sys.stdout.

You can also create a reusable function to redirect the output of any function.

from io import StringIO
import contextlib
import sys

def capture_output(func, *args, **kwargs):
"""Captures the standard output of a function.

Args:
func: The function to call.
*args: Positional arguments to pass to the function.
**kwargs: Keyword arguments to pass to the function.

Returns:
A tuple containing (return_value, stdout_output).
"""
buffer = StringIO()
with contextlib.redirect_stdout(buffer):
return_value = func(*args, **kwargs) # Call the function
stdout_output = buffer.getvalue() # Get the captured output
return return_value, stdout_output

def my_function(x, y):
print("Adding", x, "and", y)
return x + y

result, output = capture_output(my_function, 5, 3)
print(f"Result: {result}") # Output: Result: 8
print(f"Output: {output}") # Output: Adding 5 and 3

def my_other_function():
print("Hello")
print("World")

return_val, output = capture_output(my_other_function) # No arguments needed
print(f"Return value (should be None): {return_val}") # Output: Return value (should be None): None
print(f"Captured Output: {output}")

Output:

Result: 8
Output: Adding 5 and 3

Return value (should be None): None
Captured Output: Hello
World
  • This function takes another function as an argument, and captures its printed output.

Capturing Output with io.StringIO (Manual Approach)

While contextlib.redirect_stdout is preferred, understanding the underlying mechanism with io.StringIO is valuable:

from io import StringIO
import sys

original_stdout = sys.stdout # Save the original stdout
buffer = StringIO()
sys.stdout = buffer # Redirect stdout

try:
print('This will be captured, not printed to the console.')
print_output = buffer.getvalue()
print("Captured output:\n", print_output) # Still goes to the *real* console
finally:
sys.stdout = original_stdout # Restore stdout - *ESSENTIAL*

print("This is printed to the console again.")

Output:

This is printed to the console again.
  • original_stdout = sys.stdout: Crucially, we store the original stdout before modifying it.
  • sys.stdout = buffer: We redirect stdout to our StringIO object.
  • try...finally: This is essential for safety. The finally block ensures that stdout is always restored, even if an error occurs within the try block. Without this, your program might stop printing to the console entirely!
  • You can use sys.__stdout__ instead of creating an original_stdout variable.