Basic Input, Output, and String Formatting in Python

Modulo String Formatting in Python

by John Sturtz Jan 24, 2022 intermediate python

If you’re writing modern Python code with Python 3, you’ll probably want to format your strings with Python f-strings. However, if you’re working with older Python codebases, you’re likely to encounter the string modulo operator for string formatting.

If you’re reading or writing Python 2 code, it’ll help if you’re familiar with this technique. Because the syntax still works in Python 3, you might even see developers use it in modern Python codebases.

In this tutorial, you’ll learn how to:

  • Use the modulo operator (%) for string formatting
  • Convert values into specific types before inserting them into your string
  • Specify the horizontal space a formatted value occupies
  • Fine-tune the display using conversion flags
  • Specify values using dictionary mapping instead of tuples

If you’re acquainted with the printf() family of functions of C, Perl, or Java, then you’ll see that these don’t exist in Python. However, there’s quite a bit of similarity between printf() and the string modulo operator, so if you’re familiar with printf(), then a lot of the following will feel familiar.

On the other hand, if you aren’t familiar with printf(), don’t worry! You don’t need any prior knowledge of printf() to master modulo string formatting in Python.

Use the Modulo Operator for String Formatting in Python

You’ve probably used the modulo operator (%) before with numbers, in which case it computes the remainder from a division:

>>> 11 % 3

With string operands, the modulo operator has an entirely different function: string formatting.

Here’s what the syntax of the string modulo operator looks like:

<format_string> % <values>

On the left side of the % operator, <format_string> is a string containing one or more conversion specifiers. The <values> on the right side get inserted into <format_string> in place of the conversion specifiers. The resulting formatted string is the value of the expression.

Get started with an example where you call print() to display a formatted string using the string modulo operator:

>>> print("%d %s cost $%.2f" % (6, "bananas", 1.74))
6 bananas cost $1.74

In addition to representing the string modulo operation itself, the % character also denotes the beginning of a conversion specifier in the format string—in this case, there are three: %d, %s, and %.2f.

In the output, Python converted each item from the tuple of values to a string value and inserted it into the format string in place of the corresponding conversion specifier:

  • The first item in the tuple is 6, a numeric value that replaces %d in the format string.
  • The next item is the string value "bananas", which replaces %s.
  • The last item is the float value 1.74, which replaces %.2f.

The resulting string is 6 bananas cost $1.74, as demonstrated in the following diagram:

Illustration of Python string modulo operator usage
The String Modulo Operator

If there are multiple values to insert, then they must be enclosed in a tuple, as illustrated above. If there’s only one value, then you can write it by itself without the surrounding parentheses:

>>> print("Hello, my name is %s." % "Graham")
Hello, my name is Graham.

Notice also that string modulo operation isn’t only for printing. You can also format values and assign them to another string variable:

>>> welcome_sentence = "Hello, my name is %s." % "Graham"
>>> welcome_sentence
'Hello, my name is Graham.'

If you’re familiar with the functions related to printf() in the C programming language, then you might notice that the modulo string formatting syntax shown above is reminiscent of sprintf(). If you aren’t, then don’t sweat it!

Get to Know the Conversion Specifier

Different components of a conversion specifier appear in the format string and determine how values are formatted when Python inserts them into the format string.

A conversion specifier begins with a % character and can consist of a few components in a certain order:


The % character and the <type> component are required. The remaining components, shown in square brackets, are optional.

The following table summarizes what each component of a conversion specifier does:

Component Meaning
% Introduces the conversion specifier
<flags> Indicates one or more flags that exert finer control over formatting
<width> Specifies the minimum width of the formatted result
.<precision> Determines the length and precision of floating-point or string output
<type> Indicates the type of conversion to be performed

Read on for more detail on how these work.

Convert Values Using a Conversion Type

The last component of the conversion specifier, <type>, is the only required component aside from the introductory % character:


It determines the type of conversion that Python applies to the corresponding value before inserting it into the format string. Here’s a table that lists the possible conversion types:

<type> Conversion Type
d, i, u Decimal integer
x, X Hexadecimal integer
o Octal integer
f, F Floating-point
e, E E notation
g, G Floating-point or E notation
c Single character
s, r, a String
% Single '%' character

You’ll see how to use these conversion types in the following sections.

Integer Conversion Types

The d, i, u, x, X, and o conversion types correspond to integer values.

d, i, and u are functionally equivalent. They all convert the corresponding argument to a string representation of a decimal integer:

>>> "%d, %i, %u" % (42, 42, 42)
'42, 42, 42'

>>> "%d, %i, %u" % (-42, -42, -42)
'-42, -42, -42'

The value can be either positive or negative. If it’s negative, then the resulting value will start with a minus character (-).

The conversion types x and X convert to a string representation of a hexadecimal integer value, and o converts to a string representation of an octal integer value:

>>> "%x, %X" % (252, 252)
'fc, FC'

>>> "%o" % 16

Using lowercase x produces lowercase output, and using uppercase X produces uppercase output.

You can gain additional control over the resulting format by using conversion flags, which you’ll learn more about in an upcoming section.

Floating-Point Conversion Types

Conversion types f and F convert to a string representation of a floating-point number, while e and E produce a string representing E (scientific) notation:

>>> "%f, %F" % (3.14159, 3.14)
'3.141590, 3.140000'

>>> "%e, %E" % (1000.0, 1000.0)
'1.000000e+03, 1.000000E+03'

Using lowercase f and e produce lowercase output, and uppercase F and E produce uppercase output.

Under some circumstances, a floating-point operation can result in a value that’s essentially infinite. The string representation of such a number in Python is inf.

It also may happen that a floating-point operation produces a value that’s not representable as a number. Python represents this with the special floating-point value nan.

When these values are converted with the string modulo operator, the conversion type character controls the case of the resulting output. f and e produce lowercase strings, while F and E produce uppercase strings:

>>> x = float("NaN")
>>> "%f, %e, %F, %E" % (x, x, x, x)
'nan, nan, NAN, NAN'

>>> y = float("Inf")
>>> "%f, %e, %F, %E" % (y, y, y, y)
'inf, inf, INF, INF'

The only difference between the lowercase and uppercase versions of these conversion types is that they produce lowercase and uppercase string output, respectively. This difference even extends to the casing of e and E in the E notation output of floating-point numbers.

The g and G conversion types choose between floating-point or E notation output, depending on the magnitude of the exponent and what value you specify for the .<precision> component:

>>> "%g" % 3.14

>>> "%g" % 0.00000003

>>> "%G" % 0.00000003

The output is the same as for e or E if the exponent is less than -4 or not less than .<precision>. Otherwise, it’s the same as f or F. You’ll learn more about the .<precision> component later in this tutorial.

Similar to the other floating-point conversion types, g produces lowercase output, and G produces uppercase output.

Character Conversion Types

The c conversion type inserts a single character. The corresponding value may be either an integer or a single-character string:

>>> "%c" % 97

>>> "%c" % "y"

If you provide an integer, then Python translates it to the corresponding printable character. This conversion type supports conversion to Unicode characters as well:

>>> "%c" % 8721

You can use %c and pass the code point for an ASCII or Unicode character in order to render it well formatted in your string.

The conversion types s, r, and a produce string output using the built-in functions str(), repr(), and ascii(), respectively:

>>> "%s" % "Café ☕️"
'Café ☕️'

>>> "%r" % "Café ☕️"
"'Café ☕️'"

>>> "%a" % "Café ☕️"
"'Caf\\xe9 \\u2615\\ufe0f'"

When you use %a, Python converts the Unicode characters to their ASCII representation.

As you’ll see shortly, you can control the justification and padding of string output with the <width> and .<precision> conversion specifier components.

The Literal Percent Character (%%)

To insert a percent character (%) into the output, you can specify two consecutive percent characters (%%) in the format string. The first percent character introduces a conversion specifier, and the second percent character specifies that the conversion type is %.

This formatting results in a single percent character (%) in your output:

>>> "Get %d%% off on %s today only!" % (30, "bananas")
'Get 30% off on bananas today only!'

The substring %d%% in this code example represents two conversion types that follow each other:

  1. %d means a decimal integer conversion type.
  2. %% stands in for a literal percent character, which renders as %.

Note that the %% conversion type doesn’t consume any of the two <values> shown to the right of the string modulo operator (30, "bananas"). You can think of this conversion type as a way to escape the percent character in case you need to render a literal percent character in your string.

Align Data Horizontally Using Width and Precision

The <width> and .<precision> components sit in the middle of the conversion specifier:


You can use them separately or in conjunction with each other. They determine how much horizontal space a formatted value occupies by changing either the string padding or the total length of values that Python displays.

The <width> Component

You can determine the minimum width of the output field by using the <width> component. If the output is shorter than <width>, then by default, it’s right-justified in a field that is <width> characters wide and padded with ASCII space characters on the left:

>>> "%5s" % "foo"
'  foo'

>>> "%3d" % 4
'  4'

The first string, "foo", has a length of three characters. Because you use 5 as the <width> conversion specifier component, Python adds two whitespace characters before adding "foo" to build a string with a total length of five characters.

In the second example, you use the one-digit number 4 as the input value and request a string width of three characters. Therefore, Python again adds two whitespace characters before inserting the string representation of 4 to build a final string with a total length of three characters.

You can modify the justification and which padding character Python should use. You’ll learn more about how to do that in the section on conversion flags below.

If the output length is greater than <width>, then <width> has no effect:

>>> "%2d" % 1234
>>> "%d" % 1234

>>> "%2s" % "foobar"
>>> "%s" % "foobar"

Each of these examples specifies a field width of 2. But because the values you’re asking Python to format are more than two characters long, the result is the same as when you don’t specify a <width> at all.

The .<precision> Component

The .<precision> conversion specifier component affects the floating-point conversion types and character conversion types.

For the floating-point conversion types f, F, e, and E, .<precision> determines the number of digits after the decimal point:

>>> "%.2f" % 123.456789

>>> "%.2e" % 123.456789

For the floating-point conversion types g and G, .<precision> determines the total number of significant digits before and after the decimal point:

>>> "%.2g" % 123.456789

String values formatted with the s, r, and a character conversion types are truncated to the length specified by the .<precision> component:

>>> "%.4s" % "foobar"

In this example, the length of your input value "foobar" is six characters, but you’ve set .<precision> to 4. Python therefore only displays the first four characters of the input string.

You’ll probably see <width> and .<precision> used together as well:

>>> "%8.2f" % 123.45678
'  123.46'

>>> "%8.3s" % "foobar"
'     foo'

You can specify both <width> and .<precision> by using an asterisk character (*) as a placeholder. If you do that, Python takes the value for them from items in the <values> tuple:

>>> "%*d" % (10, 123)
'       123'

>>> "%.*d" % (10, 123)

>>> "%*.*d" % (10, 5, 123)
'     00123'

You probably won’t need to use this when your <width> value is a constant. There isn’t any functional difference between using placeholders as in the example above and directly adding the values:

>>> "%10d" % 123
'       123'

>>> "%.10d" % 123

>>> "%10.5d" % 123
'     00123'

Using placeholder asterisks becomes more interesting when you specify width or precision using variables:

>>> for i in range(3):
...     w = int(input("Enter width: "))
...     print("[%*s]" % (w, "foo"))
Enter width: 2
Enter width: 4
[ foo]
Enter width: 8
[     foo]

With this syntax, you can determine width and precision at runtime, which means they can potentially change from one execution to another.

Fine-Tune Your Output With Conversion Flags

You can specify optional conversion flags right after the initial % character:


These allow you to control the display of certain conversion types in more detail. The <flags> component of a conversion specifier can include any of the characters shown in the following table:

Character Controls
# Display of base or decimal point for integer and floating-point values
0 Padding of values that are shorter than the specified field width
- Justification of values that are shorter than the specified field width
+ Display of leading sign for numeric values
' ' (space) Display of leading sign for numeric values

The following sections explain how conversion flags operate in greater detail.

The Hash Flag (#)

The # flag causes base information to be included in the formatted output for the octal and hexadecimal conversion types. For the o conversion type, this flag adds a leading "0o". For the x and X conversion types, it adds a leading "0x" or "0X":

>>> "%#o" % 16

>>> "%#x" % 16, "%#X" % 16
('0x10', '0X10')

The # flag is ignored for the decimal conversion types d, i, and u.

For floating-point values, the # flag forces the output to always contain a decimal point. Ordinarily, floating-point values will not contain a decimal point if there aren’t any digits after it. This flag forces the inclusion of a decimal point:

>>> "%.0f" % 123
>>> "%#.0f" % 123

>>> "%.0e" % 123
>>> "%#.0e" % 123

Forcing the inclusion of a decimal point by using the # flag also works for values displayed in E notation, as shown in the example code snippet.

The Zero Flag (0)

When a formatted numeric value is shorter than the specified field width, the default behavior is to pad the field with ASCII space characters to the left of the value. The 0 flag causes padding with "0" characters instead:

>>> "%05d" % 123

>>> "%08.2f" % 1.2

The 0 flag can be used with all the numeric conversion types: d, i, u, x, X, o, f, F, e, E, g, and G.

The Hyphen-Minus Flag (-)

When a formatted value is shorter than the specified field width, it’s usually right-justified in the field. The hyphen-minus (-) flag causes the value to be left-justified in the specified field instead:

>>> "%-5d" % 123
'123  '

>>> "%-8.2f" % 123.3
'123.30  '

>>> "%-*s" % (10, "foo")
'foo       '

You can use the - flag with the string conversion types s, a, and r, as well as all the numeric conversion types. For numeric types, if both 0 and - are present, then 0 is ignored.

The Plus Flag (+)

By default, positive numeric values do not have a leading sign character. The + flag adds a plus character (+) to the left of the numeric output:

>>> "%+d" % 3

>>> "%+5d" % 3
'   +3'

>>> "%5d" % 3
'    3'

When comparing the final two outputs, you might notice that Python takes the + character that you added with this flag into account when calculating the width of the output.

This flag doesn’t affect negative numeric values, which always have a leading minus character (-). Just like with the plus character, Python also takes the minus character into account when calculating the width of the output.

The Space Character Flag (' ')

The space character flag (' ') adds a space character in front of positive numeric values:

>>> "% d" % 3
' 3'

Just like the + flag, this flag also doesn’t affect negative numeric values, which always have a leading minus character (-):

>>> "% d" % 3
' 3'

>>> "% d" % -3
>>> "%d" % -3

Using either the + flag or the space character flag can help you align a mix of positive and negative values consistently.

Specify Values by Dictionary Mapping

You can specify the <values> inserted into the format string as a dictionary instead of a tuple. In that case, each conversion specifier must contain one of the dictionary keys in parentheses immediately following the % character:

>>> "%d %s cost $%.2f" % (6, "bananas", 1.74)
'6 bananas cost $1.74'

>>> data = {"quantity": 6, "item": "bananas", "price": 1.74}
>>> template = "%(quantity)d %(item)s cost $%(price).2f"
>>> template % data
'6 bananas cost $1.74'

By using this technique, you can specify the inserted values in any order:

>>> data = {"quantity": 6, "item": "bananas", "price": 1.74}

>>> ad_1 = "%(quantity)d %(item)s cost $%(price).2f"
>>> ad_1 % data
'6 bananas cost $1.74'

>>> ad_2 = "You'll pay $%(price).2f for %(item)s, if you buy %(quantity)d"
>>> ad_2 % data
"You'll pay $1.74 for bananas, if you buy 6"

All the conversion specifier components shown above—<flags>, <width>, .<precision>, and <type>—still work in the same way as they did when you input the values using a tuple. You can use any of them when you specify <values> as a dictionary:

>>> "Quantity: %(quantity)03d" % data
'Quantity: 006'

>>> "Item:     %(item).5s" % data
'Item:     banan'

In the example code snippet above, you’ve used the 0 flag combined with <width> in the first example, and .<precision> with a value of 5 in the second example.

Specifying the input values by dictionary mapping is especially useful if you want to define the data only once and you might switch the order of display in different outputs.


At this point, you’ve learned a lot about the string modulo operator. It’s an older string formatting method in Python. Because the technique still works and has been commonly used in Python 2, it’s helpful to understand the basics of this syntax.

In this tutorial, you’ve learned how to:

  • Use the modulo operator (%) for string formatting
  • Convert values into specific types before inserting them into your string
  • Specify the horizontal space that a formatted value occupies
  • Fine-tune the display using conversion flags
  • Specify values using dictionary mapping instead of tuples

As versatile as the string modulo operator is, Python provides newer ways to format string data that are even better: the string .format() method and the formatted string literal, which is generally called f-string.

🐍 Python Tricks 💌

Get a short & sweet Python Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

Python Tricks Dictionary Merge

About John Sturtz

John Sturtz John Sturtz

John is an avid Pythonista and a member of the Real Python tutorial team.

» More about John

Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:

Master Real-World Python Skills With Unlimited Access to Real Python

Join us and get access to hundreds of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

Master Real-World Python Skills
With Unlimited Access to Real Python

Join us and get access to hundreds of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

What Do You Think?

Real Python Comment Policy: The most useful comments are those written with the goal of learning from or helping out other readers—after reading the whole article and all the earlier comments. Complaints and insults generally won’t make the cut here.

What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.

Keep Learning

Related Tutorial Categories: intermediate python