Search code examples
python-3.xformattingipythonpython-3.7

ipython: print numbers with thousands separator


I am using ipython 5.8.0 on Debian 10.

This is how output looks like:

In [1]: 50*50
Out[1]: 2500

Is it possible to configure ipython to print all numbers with thousands separators? ie:

In [1]: 50*50
Out[1]: 2'500

In [2]: 5000*5000
Out[2]: 25'000'000

And perhaps, is it possible to make ipython also understand thousands separators on input?

In [1]: 5'000*5'000
Out[1]: 25'000'000

UPDATE

The accepted answer from @Chayim Friedman works for integers, but does not work for float:

In [1]: 500.1*500
Out[1]: 250050.0

Also, when it works, it uses , as the character for thousand separator:

In [1]: 500*500
Out[1]: 250,000

Can I use ' instead?


Solution

  • Using ' as thousands separator in input is quite problematic because Python uses ' to delimit strings, but you can use _ (PEP 515, Underscores in Numeric Literals):

    IPython input thousand separators

    Regarding output, this is slightly harder, but can be done using IPython extensions.

    Put the following Python code in a new file at ~/.ipython/extensions/thousands_separator.py:

    default_int_printer = None
    
    def print_int(number, printer, cycle):
        printer.text(f'{number:,}') # You can use `'{:,}'.format(number)` if you're using a Python version older than 3.6
    
    def load_ipython_extension(ipython):
        global default_int_printer
    
        default_int_printer = ipython.display_formatter.formatters['text/plain'].for_type(int, print_int)
    
    def unload_ipython_extension(ipython):
        ipython.display_formatter.formatters['text/plain'].for_type(int, default_int_printer)
    

    This code tells IPython to replace the default int formatter with one that prints thousand separators when this extension is loaded, and restore the original when it is unloaded.

    Edit: If you want a different separator, for instance ', replace the f'{number:,}' with f'{number:,}'.replace(',', "'").

    You can load the extension using the magic command %load_ext thousands_separator and unload it using %unload_ext thousands_separator, but if you want it always, you can place it in the default profile.

    Run the following code in the terminal:

    ipython3 profile create
    

    It will report that a file ~/.ipython/profile_default/ipython_config.py was created. Enter it, and search for the following string:

    ## A list of dotted module names of IPython extensions to load.
    #c.InteractiveShellApp.extensions = []
    

    Replace it with the following:

    # A list of dotted module names of IPython extensions to load.
    c.InteractiveShellApp.extensions = [
        'thousands_separator'
    ]
    

    This tells IPython to load this extension by default.

    Done!

    enter image description here

    Edit: I saw that you want to a) use ' as separator, and b) do the same for floats:

    Using different separator is quite easy: just str.replace():

    def print_int(number, printer, cycle):
        printer.text(f'{number:,}'.replace(',', "'"))
    

    Doing the same for floats is also easy: just setup print_int so it prints floats to. I also suggest to change the name to print_number.

    Final code:

    default_int_printer = None
    default_float_printer = None
    
    def print_number(number, printer, cycle):
        printer.text(f'{number:,}'.replace(',', "'"))
    
    def load_ipython_extension(ipython):
        global default_int_printer
        global default_float_printer
    
        default_int_printer = ipython.display_formatter.formatters['text/plain'].for_type(int, print_number)
        default_float_printer = ipython.display_formatter.formatters['text/plain'].for_type(float, print_number)
    
    def unload_ipython_extension(ipython):
        ipython.display_formatter.formatters['text/plain'].for_type(int, default_int_printer)
        ipython.display_formatter.formatters['text/plain'].for_type(float, default_float_printer)