string formatting specifications

Gribouillis 3 Tallied Votes 3K Views Share

The syntax of the str.format() method described in the python 2.6 documentation looks both powerful and complex. The idea of this thread is to start a collection of nice formatting examples which will ease the task of mastering this function. Please post useful examples, and document them :)

rabbits = {
    "flopsy" : 1.0/3,
    "mopsy" : 576.0/7,
    "cotton tail": .76/5,
    "peter": 300000.0/37,
}

for name in sorted(rabbits):
    # 13 and 10 are field width, > means right align,
    # .4f means a float with 4 digits after '.'
    print("{name:13}:{score:>10.4f}".format(name=name, score=rabbits[name]))

"""my output ---->
cotton tail  :    0.1520
flopsy       :    0.3333
mopsy        :   82.2857
peter        : 8108.1081
"""
Gribouillis 1,391 Programming Explorer Team Colleague

Same example with computed field's width:

rabbits = {
    "flopsy" : 1.0/3, "mopsy" : 576.0/7, "cotton tail": .76/5, "peter": 300000.0/37,
}

nwidth = 1 + max(len(name) for name in rabbits)

for name in sorted(rabbits):
    # the name field's width is passed as argument to format
    print("{name:{namewidth}}:{score:>10.4f}".format(
          name = name, score = rabbits[name], namewidth = nwidth))

"""my output ---->
cotton tail :    0.1520
flopsy      :    0.3333
mopsy       :   82.2857
peter       : 8108.1081
"""
Gribouillis 1,391 Programming Explorer Team Colleague

The floating point precision can be passed as argument too

rabbits = {
    "flopsy" : 1.0/3, "mopsy" : 576.0/7, "cotton tail": .76/5, "peter": 300000.0/37,
}

nwidth = 1 + max(len(name) for name in rabbits)

for name in sorted(rabbits):
    # the floating point precision is passed as argument to format
    print("{name:{namewidth}}:{score:>10.{precision}f}".format(
          name = name, score = rabbits[name],
          namewidth = nwidth, precision = 2))

"""my output ---->
cotton tail :      0.15
flopsy      :      0.33
mopsy       :     82.29
peter       :   8108.11
"""
Gribouillis 1,391 Programming Explorer Team Colleague

White space in the fields can be filled with a single character.

rabbits = {
    "flopsy" : 1.0/3, "mopsy" : 576.0/7, "cotton tail": .76/5, "peter": 300000.0/37,
}

nwidth = 1 + max(len(name) for name in rabbits)

for name in sorted(rabbits):
    # A single character can be used before the alignment sign (< ^ = or >)
    # to fill white space in each field.
    print("{name:{fill}<{namewidth}}{score:{fill}>20.{precision}f}".format(
          name = name, score = rabbits[name],
          namewidth = nwidth, precision = 2, fill="-"))

"""my output ---->
cotton tail-----------------0.15
flopsy----------------------0.33
mopsy----------------------82.29
peter--------------------8108.11
"""
Gribouillis 1,391 Programming Explorer Team Colleague

For complex templates, we can use functools.partial to set the value of some of the template's arguments. This results in a cleaner code

import functools
rabbits = {
    "flopsy" : 1.0/3, "mopsy" : 576.0/7, "cotton tail": .76/5, "peter": 300000.0/37,
}

template = "{name:{fill}<{namewidth}}{score:{fill}>20.{precision}f}"

nwidth = 1 + max(len(name) for name in rabbits)
# The use of functools.partial allows us to give a value to a subset of
# the template's arguments.
rabbit_line = functools.partial(template.format,
             namewidth = nwidth, precision = 2, fill="-")

for name in sorted(rabbits):
    print(rabbit_line(name = name, score = rabbits[name]))

"""my output ---->
cotton tail-----------------0.15
flopsy----------------------0.33
mopsy----------------------82.29
peter--------------------8108.11
"""
vegaseat 1,735 DaniWeb's Hypocrite Team Colleague

Access a dictionary directly ...

# a food:price dictionary
food_dict = {
'soymilk' : 3.69,
'butter' : 1.95,
'bread' : 2.19,
'cheese' : 4.39
}

# pull one key item like 'bread' out of the dictionary
print("Bread = ${bread:4.2f}".format(**food_dict))  # Bread = $2.19
Gribouillis 1,391 Programming Explorer Team Colleague

Formatting specifications for datetime.datetime objects differ from the standard ones. They obey the rules of datetime.datetime.strftime. Each class can define its own formatting specifications through its __format__ method.

from datetime import datetime
import sys
from os.path import getmtime

python = sys.executable
pytime = datetime.fromtimestamp(getmtime(python))

print("My python executable was last modified on {t:%b %d %Y} at {t:%H:%M:%S}."
         .format(t=pytime))

"""my output ---->
My python executable was last modified on Aug 22 2009 at 18:38:31.
"""
Gribouillis 1,391 Programming Explorer Team Colleague

The different floating point formatting types (exponential, fixed, general, number and percentage).

from math import pi

ftypes = "e E f F g G n %".split()

header = "".join("{{types[{i}]:^10}}".format(i = i) for i in range(len(ftypes)))
line = "".join("{{val:^10.2{tp}}}".format(tp = t) for t in ftypes)

print(line)
print('')
print(header.format(types = ftypes))
print("-" * 80)
for x in (pi*pi, 1.0/pi, pi **(-9), pi**17):
    print(line.format(val = x))

""" my output ---->
{val:^10.2e}{val:^10.2E}{val:^10.2f}{val:^10.2F}{val:^10.2g}{val:^10.2G}{val:^10.2n}{val:^10.2%}

    e         E         f         F         g         G         n         %     
--------------------------------------------------------------------------------
 9.87e+00  9.87E+00    9.87      9.87      9.9       9.9       9.9     986.96%  
 3.18e-01  3.18E-01    0.32      0.32      0.32      0.32      0.32     31.83%  
 3.35e-05  3.35E-05    0.00      0.00    3.4e-05   3.4E-05   3.4e-05    0.00%   
 2.83e+08  2.83E+08 282844563.59282844563.59 2.8e+08   2.8E+08   2.8e+08  28284456358.65%
"""

Note that for large numbers, the exponential notation is superior. Also note that for the g and G type, the precision .2 is interpreted as a number of significant digits.

Gribouillis 1,391 Programming Explorer Team Colleague

For non numeric types, the precision is interpreted as a maximum field's width (the normal field's width is a minimum width).

class Animal():
    def __init__(self, name):
      self.name = name
    def __repr__(self):
        return "Animal({0})".format(self.name)

animals = [ Animal(name) for name in "python lion giraffe antelope gnu".split() ]

# Each field has a minimum width (10) and a maximum width (13).
# The field's content is truncated if necessary.
# this works only for non numeric fields.
template = " | ".join("{{seq[{index}]:10.13}}".format(index = i) for i in range(len(animals)))


print("template: " + template)
print(template.format(seq = animals))

""" my output ---->
template: {seq[0]:10.13} | {seq[1]:10.13} | {seq[2]:10.13} | {seq[3]:10.13} | {seq[4]:10.13}
result:
Animal(python | Animal(lion) | Animal(giraff | Animal(antelo | Animal(gnu)
"""
Gribouillis 1,391 Programming Explorer Team Colleague

Adding alignment and field widths capabilities. Since objects like datetime.datetime have their custom formatting syntax, they don't support alignment and field width capabilities.
For example, formatting a datetime with {time:>20%H:%M:%S} won't cause the time to be right aligned in a field of length 20. Instead, the string >20 will be printed verbatim.
Using the __format__ method, it's easy to write a general wrapper class Align which gives other objects the ability to handle alignment and field width in formatting operations.

#!/usr/bin/env python
from functools import update_wrapper 
import re 
_align_re =re .compile (r"^([<^>]?[0-9]*)(?:[.][0-9]*)?")

def with_align (format_method ,maxwidth =True ):
  """Decorator for custom __format__ methods.
  Gives a __format__ method the ability to handle
  alignment character and field min width and max width
  with the syntax [align][minwidth].[maxwidth]
  similar to the standard formatting syntax
  """
  def wrapper (self ,format_spec ):
    match =_align_re .match (format_spec )
    index =match .end (0 if maxwidth else 1 )
    match ,format_spec =format_spec [:index ],format_spec [index :]
    data_str =format_method (self ,format_spec )
    if match :
      data_str =("{0:"+match +"}").format (data_str )
    return data_str 
  update_wrapper (wrapper ,format_method )
  return wrapper 

class Align (object ):
  """Wrapper class to allow format alignment.
  Wrapping an object in Align adds alignment capabilities
  to the object in formatting operations.
  Example:  "Time is {0:>10%H%M%S}".format(Align(datetime.now()))
  """
  def __init__ (self ,wrapped_obj ):
    self .wrapped_obj =wrapped_obj 

  @with_align 
  def __format__ (self ,format_spec ):
    return ("{0:"+format_spec +"}").format (self .wrapped_obj )

def dtime ():
  "Returns a datetime object for testing purposes"
  from sys import executable as python 
  from datetime import datetime 
  from os .path import getmtime 
  return datetime .fromtimestamp (getmtime (python ))

def atest ():
  "Test of formatting a datetime object with alignment"
  print ("Test without Align:")
# The alignment and field width can't be used for datetime
  print ("Time was: {time:>20%H:%M:%S}".format (time =dtime ()))
# Wrapping the datetime object in an Align object allows
# to handle the alignment specification
  print ("Test with Align:")
  print ("Time was: {time:>20%H:%M:%S}".format (time =Align (dtime ())))

atest ()
""" my output ---->
Test without Align:
Time was: >2018:38:31
Test with Align:
Time was:             18:38:31  # correctly aligned
"""

The Align wrapper can be used for other kind of objects, and the decorator can be used to give the desired properties to user defined classes without the need to wrap the object in an Align wrapper.

Gribouillis 1,391 Programming Explorer Team Colleague

Partial formatting.
Normally, when you call format on a template string like "{x:<15.{precision}e}{y:<15.{precision}e}" , all the template's arguments must be passed to format, otherwise, a KeyError is raised (or IndexError for positional arguments). Here we need to pass 3 keyword arguments x, y and precision.
Using a subclass of string.Formatter, one can implement a partial formatting, with missing arguments. For example in the above example, we could instantiate first the precision to get the template "{x:<15.3e}{y:<15.3e}" , and then format this string with a pair of values x and y. Here is the code

#!/usr/bin/env python
from string import Formatter 

class MissingField (object ):
  "Helper class to implement partial formatting"
  def __init__ (self ,name ):
    self .name =name 

class MissingDict (dict ):
  "Helper class to implement partial formatting"
  def __missing__ (self ,key ):
    return MissingField (key )

class PartialFormatter (Formatter ):
  "Formatter class which allows missing keys in formatting"
  def format (self ,format_string ,*args ,**kwd ):
    return self .vformat (format_string ,args ,MissingDict (**kwd ))
  def format_field (self ,value ,format_string ):
    if isinstance (value ,MissingField ):
      s =":"+format_string if format_string else ""
      result ="{"+value .name +s +"}"
    else :
      result =Formatter .format_field (self ,value ,format_string )
    return result 

def atest ():
  "Testing partial formatting"
  from math import sin 
  template ="{x:<15.{precision}e}{y:<15.{precision}e}"
  print ("Initial template: "+template )
  pf =PartialFormatter ()
  ptemplate =pf .format (template ,precision =3 )
  print ("Partial template: "+ptemplate )
  print ("")
  for i in range (5 ):
    x =0.1 *i 
    print (ptemplate .format (x =x ,y =sin (x )))

atest ()
""" my output --->
Initial template: {x:<15.{precision}e}{y:<15.{precision}e}
Partial template: {x:<15.3e}{y:<15.3e}

0.000e+00      0.000e+00      
1.000e-01      9.983e-02      
2.000e-01      1.987e-01      
3.000e-01      2.955e-01      
4.000e-01      3.894e-01 
"""
Gribouillis 1,391 Programming Explorer Team Colleague

Displaying tabular data
Here we use format to display tabular data. Consider reusing the Tabular class in your programs, as shown in the function 'test_func' below

#!/usr/bin/env python
from os import linesep as LS 

class Tabular (object ):
  def __init__ (self ,columns ,gen_data ):
    """Create a Tabular object for formatting a table.
    @ columns is a sequence of triples (col_title, col_align, tpdata)
      - col_title is the string at the top of the column. It's width
        determines the column witdth
      - col align is "<" or "^" or ">"
      - tpdata is a template to format the column entries, with a single
        positional argument 0.
    @ gen_data is a generator of the table rows, which are tuples of
      values to be formatted.
    """
    self .gen_data =gen_data 
    columns =list (columns )
    self .titles =[]
    self .templates =[]
    for col_title ,col_align ,tpdata in columns :
      tpcell ="{{0:{a}{w}.{w}}}".format (a =col_align ,w =len (col_title ))
      self .titles .append (col_title )
      self .templates .append ((tpcell ,tpdata ))
    self .width =sum (len (x )for x in self .titles )+len (self .titles )-1 
  def delimit (self ,msg ):
    return "|"+msg +"|"
  def top (self ):
    return "-"*(self .width +2 )
  bottom =top 
  def lines (self ):
    yield self .top ()
    yield self .delimit ("|".join (self .titles ))
    yield self .top ()
    for line_data in self .gen_data :
      line =[]
      for (tpcell ,tpdata ),data in zip (self .templates ,line_data ):
        s =tpcell .format (tpdata .format (data ))
        line .append (s )
      yield self .delimit ("|".join (line ))
    yield self .bottom ()
  def __format__ (self ,format_spec ):
    if format_spec :
      template ="{0:"+format_spec +"}"
      return LS .join (template .format (line )for line in self .lines ())
    else :
      return LS .join (self .lines ())

def test_func ():
  "Test the formatting of tables using Tabular"
  from math import sin 
  dx =0.05 
  table =Tabular (
  [("{0:^5}".format ("i"),"<"," {0}"),
  ("{0:^13}".format ("parameter"),"^","{0:.3e}"),
  ("{0:^20}".format ("value"),"^","{0:.3e}"),],# columns
  ((i ,i *dx ,sin (i *dx ))for i in range (5 ))# rows
  )
# formatting and printing the table
# Look how the table is centered in a field of 72 characters.

  print "{ls}{title:^{width}}{ls}{tbl:^{width}}".format (
  tbl =table ,title ="Table of sines.",width =72 ,ls =LS )
test_func ()
""" my output --->

                            Table of sines.                             
               ------------------------------------------               
               |  i  |  parameter  |       value        |               
               ------------------------------------------               
               | 0   |  0.000e+00  |     0.000e+00      |               
               | 1   |  5.000e-02  |     4.998e-02      |               
               | 2   |  1.000e-01  |     9.983e-02      |               
               | 3   |  1.500e-01  |     1.494e-01      |               
               | 4   |  2.000e-01  |     1.987e-01      |               
               ------------------------------------------  
"""
Gribouillis 1,391 Programming Explorer Team Colleague

Formatting with colors in a linux console
The following module combines formatting strings and ansi escape sequences to support printing with color in a linux console. After importing the module, type help(termstyle) for full documentation.

#!/usr/bin/env python
# termstyle.py 0.9 (linux only)
"""This module implements a system using ANSI escape codes
to display colors in a terminal.

Example:
    from termstyle import Style, clear
    blue = Style("blue", "underline")
    clear()
    print( blue("Hello world"))
    print( "Velocity: {0:.3e} m/s" .format( blue(2.45589) ))
"""
CSI = "\x1b["
COLORS = "black red green yellow blue magenta cyan white".split()
M_DICT = dict((w, str(30 + i))
    for (i, w) in enumerate(COLORS)) # foreground colors
M_DICT.update(("-" + w, str(40 + i))
    for (i, w) in enumerate(COLORS)) # background colors
EFFECTS = """
    reset bold faint italic underline slow fast negative conceal
    crossout normal underlineoff blinkoff positive reveal
""" .strip().split()
M_DICT.update((w, str(i)) for i, w in enumerate(EFFECTS))

__all__ = ["Style", "clear"]

class Style(object):
    """Style(*effects) --> a new Style object.
@ effects is a sequence of
    -words like "red" (call Style.effects() to see all the words)
    -pairs (line, column) describing a terminal position
    -other Style objets

* If 'effects' contains a Style instance, other effects are ignored
  and a copy of the first instance met is built.
* If effects contains position pairs, only the last pair is used.
* Call Style.colors() to see all foreground colors.
* A background color is passed by putting "-" at the beginning of
  the color name. For example "-white"

Effects can be disabled for all Styles, or for a Style instance
by setting:
    Style.enable = False
    instance.enable = False
"""
    enable = True

    @classmethod
    def effects(cls):
        "return an iterator over all possible named effects."
        return iter(M_DICT)

    @classmethod
    def colors(cls):
        "return an iterator over all foreground colors."
        return iter(COLORS)

    def __init__(self, *effects):
        "see class documentation"
        self.template = self.ansi_template(effects)

    def ansi_template(self, effects):
        "builds an ANSI template string from the sequence of effects"

        controls = []
        position = ""
        for a in effects:
            if isinstance(a, Style):
                return a.template
            elif isinstance(a, tuple):
                line, column = (str(int(x)) for x in a)
                position = ''.join((CSI, line, ";", column, "H"))
            else:
                controls.append(M_DICT[a])
        if controls:
            template = ''.join(
                (position, CSI, ';'.join(controls), "m", "{text}", CSI, "0m"))
        else:
            template = ''.join((position, "{text}"))
        if position:
            template = ''.join((CSI, "s", template, CSI, "u"))
        return template

    def __call__(self, content):
        """Calling a Style instance creates a StyledContent instance
        which can be printed in a terminal.
        If self.enable is False, content is returned instead"""
        return StyledContent(self, content) if self.enable else content

class StyledContent(object):
    """StyleContent(style, content) --> a StyleContent instance.
    This is the type returned by Style.__call__.
    """
    def __init__(self, style, content):
        "See class documentation"
        self.content = content
        self.style = style

    def __format__(self, format_spec):
        """Formatting a StyleContent instance is equivalent to
        formatting the content, with ANSI escape sequences added
        for terminal output"""
        return self.style.template.format(
                    text = ("{0:" + format_spec + "}").format(self.content))

    def __str__(self):
        """Calling str() on a StyleContent instance is equivalent to
        calling str() on the content, with ANSI escape sequences added
        for terminal output"""
        return self.style.template.format(text = self.content)

def clear():
  "clear the teminal"
  print(CSI + "2J")

if __name__ == "__main__":
    def test_code():
        "test function for module termstyle"
        from datetime import datetime
        from math import cos

        green = Style("-white", "green", "underline")
        blue = Style("blue", "bold")
        yellow = Style("yellow")
        cyan = Style("cyan")
        middle = Style((10, 20))

        clear()
        print( "The current time is {0:%H:%M:%S}.".format(blue(datetime.now())))
        print("")
        for i in range(5):
            x = 0.1 * i
            print("{0:12.3e}{1:12.3e}".format(yellow(x), cyan(cos(x))))
        print( middle("This is a {w} of terminal styles." .format(
                    w = green("test"))))


    test_code()
""" my output  (without the colors) --->

                   This is a test of terminal styles.






The current time is 16:18:01.

   0.000e+00   1.000e+00
   1.000e-01   9.950e-01
   2.000e-01   9.801e-01
   3.000e-01   9.553e-01
   4.000e-01   9.211e-01
"""
Gribouillis 1,391 Programming Explorer Team Colleague

Colors in a windows console
This is a little bit off topic, but since the previous post shows you how to print in colors in a linux console, well, I found a link of someone who prints in colors in a windows console using module ctypes http://www.burgaud.com/bring-colors-to-the-windows-console-with-python/ . A nice idea would be to wrap his code in Style objects as in the above snippet and create a cross-platform way to print in colors in a console !

edit: since this post was written, module colorama appeared, which achieves this goal.

Gribouillis 1,391 Programming Explorer Team Colleague

Some new formatting examples in this post by vegaseat.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.