Customizing Display Output with sys.displayhook

Customizing Display Output with sys.displayhook

When you type an expression in the Python interactive shell, the system needs a way to show you the result. This is where sys.displayhook steps in. It’s a hidden little function that Python calls every time an expression returns a value that’s not None. Its job? To display that value in a clean, readable form.

By default, sys.displayhook does three things: it prints the representation of the result, saves it into the special variable _ (underscore), and then flushes the output buffer. This might sound trivial, but it’s what makes the REPL feel so interactive and responsive.

Here’s an example of what happens behind the scenes:

import sys

# The default displayhook function
def displayhook(value):
    if value is not None:
        builtins._ = value
        print(repr(value))

# Imagine this is called automatically after every expression in interactive mode
displayhook([1, 2, 3])

Notice how the system assigns the value to _. This is why you can do stuff like this:

>>> 42
42
>>> _ + 8
50

That little underscore is a convenience variable. It’s not just a quirky Python thing; it’s powered by sys.displayhook. Without it, you’d have to assign your output to variables all the time to reuse it.

But what if you want to see how sys.displayhook works internally? You can grab the current one and check it out:

import sys
original_hook = sys.displayhook
print(original_hook)

It won’t print the source, but it shows you it’s a built-in function. To get the actual source code, you’d have to look into CPython’s implementation in C. The important thing is that this function is replaceable. You can swap it out with your own function if you want to control how results are presented.

That’s the key insight here: sys.displayhook controls exactly what happens with the results you see in your interactive session. It’s the final step after an expression evaluates, and before you get the prompt back. If you want to suppress output, change formatting, or even log every output somewhere, this is your entry point.

One subtlety worth mentioning: sys.displayhook only deals with the results of expressions, not statements. For example, if you run a function that returns None, nothing gets printed, because displayhook ignores None values. This behavior makes sense because statements typically produce side effects rather than values to inspect.

To demonstrate, try this in the interactive interpreter:

>>> def foo():
...     print("Hello world")
...
>>> foo()
Hello world
>>> foo()

Notice the function’s print happens, but the return value (None) doesn’t trigger displayhook. So, understanding this behavior helps you grasp when and why output appears in the REPL.

Another interesting point: the output is always the repr() of the value. That’s why lists show up with brackets, strings with quotes, and so on. If you want to pretty-print complex objects, you might want to override sys.displayhook to call pprint.pprint() instead of plain repr().

Here’s a quick example of what that might look like:

import sys
import pprint

def pretty_displayhook(value):
    if value is not None:
        builtins._ = value
        pprint.pprint(value)

sys.displayhook = pretty_displayhook

Now every expression result will be nicely formatted. This is especially handy when working with nested dictionaries or lists that clutter the screen otherwise.

Of course, messing with sys.displayhook can be dangerous if you don’t restore it properly. If you break it or forget to reassign the original function, your REPL output might vanish or behave strangely. So, always save the original first and restore it when you’re done experimenting:

import sys

original_hook = sys.displayhook

# Your custom hook goes here

sys.displayhook = original_hook

That’s the gist of how sys.displayhook shapes your interactive experience. Next up, we’ll see how to take control of it and make your own display behavior—because sometimes the default just isn’t good enough, especially when debugging or building custom REPLs. But before that, make sure you grasp how the default mechanism ties together the output you see and the underscore variable you rely on every day.

Keep in mind that Python’s interactive shell is just one place where displayhook shines. IPython, for instance, replaces it with something much fancier, enabling rich HTML output, inline images, and more. The ability to swap out this function is what opens the door for those powerful enhancements.

Also, the _ variable is updated each time displayhook is called, but only for non-None results. If you assign to _ manually, the shell won’t overwrite it until the next expression result appears, which can lead to subtle bugs if you aren’t aware of this interaction.

So, the takeaway: sys.displayhook is a small but mighty piece of Python’s interactive layer. Understanding it lets you customize how output looks and behaves, giving you control over the REPL that most developers never think about. If you want to build better tools, debugging utilities, or just have more fun in the shell, this is a great place to start tinkering.

But before diving into customization, it’s critical to remember that displayhook operates synchronously after each expression evaluation. If your replacement function performs slow operations or blocks, you’ll notice a lag every time you hit Enter. This makes performance considerations important when designing a custom hook.

Finally, a quick note about exceptions: displayhook is not involved in printing tracebacks or error messages. Those are handled elsewhere by sys.excepthook. So your custom displayhook won’t interfere with error output, keeping concerns nicely separated.

With all this in mind, you’re ready to start experimenting with your own sys.displayhook. Next, we’ll tweak it to create personalized display behavior that fits your workflow perfectly. Maybe you want color, or logging, or even suppression of certain outputs. Whatever it is, it’s just a function away.

Let’s look at how to do that—

Tweaking sys.displayhook to create your own display behavior

To create your own sys.displayhook, you simply define a function that accepts one argument—the value to display—and then assign it to sys.displayhook. The function should handle the value appropriately, typically printing something or logging it, and also updating the special underscore variable _ in builtins. If you forget to update _, the interactive shell won’t have the last result saved for you.

Here’s a minimal custom displayhook that shows the value in uppercase if it’s a string, otherwise falls back to the default behavior:

import sys
import builtins

def uppercase_displayhook(value):
    if value is not None:
        builtins._ = value
        if isinstance(value, str):
            print(value.upper())
        else:
            print(repr(value))

sys.displayhook = uppercase_displayhook

Try this out in your interactive session after running the above code:

>>> "hello world"
HELLO WORLD
>>> [1, 2, 3]
[1, 2, 3]
>>> _
[1, 2, 3]

You can see that the underscore variable still works as expected, preserving the last output, but the display changes only for strings. This example is simple, but it illustrates the core idea: control the formatting, keep the state, and print the output.

Another common tweak is to suppress output altogether for certain values. For example, if you want to suppress displaying None or empty lists, you can customize the hook like this:

def suppress_empty_displayhook(value):
    if value is not None and value != []:
        builtins._ = value
        print(repr(value))

sys.displayhook = suppress_empty_displayhook

Now, if you evaluate an empty list, nothing will be printed:

>>> []
>>> [1, 2]
[1, 2]

When building more advanced displayhooks, you can also add logging or even send the output somewhere else. Here’s an example that logs every expression result to a file while still printing it normally:

def logging_displayhook(value):
    if value is not None:
        builtins._ = value
        with open("output.log", "a") as f:
            f.write(repr(value) + "n")
        print(repr(value))

sys.displayhook = logging_displayhook

Every time you enter an expression, its result will be appended to output.log. This can be handy when you want to keep a history of your interactive session outputs without scrolling back endlessly.

One last trick: you can even colorize output in terminals that support ANSI escape codes. Let’s say you want all integers to appear in green:

def color_displayhook(value):
    if value is not None:
        builtins._ = value
        if isinstance(value, int):
            print(f"33[92m{value}33[0m")  # Green text
        else:
            print(repr(value))

sys.displayhook = color_displayhook

Try typing integers and other types to see the difference:

>>> 123
123  # in green
>>> "text"
'text'  # normal

Just remember to reset the color after printing, or your terminal might stay colored unexpectedly.

When you’re done customizing, don’t forget to restore the original displayhook to avoid confusing behavior in your shell:

sys.displayhook = original_hook

Experimenting with sys.displayhook can be a powerful way to tailor the interactive experience. Whether you want prettier output, logging, filtering, or even integration with other tools, this small hook is your gateway.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *