Using !s vs. :s to format a string in Python

Question:

I’m really curious about :s format string in Python 3. The documentation says !s is conversion and that :s is format_spec.

It also says !s will apply str(), but it doesn’t say anything similar about :s. I think there’s no significant difference between them, but I want to be sure. Can anyone clarify these?

Some code example:

print("{!s}".format("this"))
print("{:s}".format("that"))
# I want to be sure that these two are processed identically internally

It’s still confusing, but let me wrap up with my own (layperson’s) words.

  1. type("whatever".format) is always str.
  2. Use !s if you want convert the object into str before formatting.
  3. :s means that the object(or the converted object) will be treated as str during some internal formatting process. It’s the default format_spec.

Is anything wrong here?

Asked By: Sangbok Lee

||

Answers:

From the documentation, Python String Formatting:

The field_name is optionally followed by a conversion field, which is preceded by an exclamation point ‘!’, and a format_spec, which is preceded by a colon ‘:’.

So no, it’s not the same.

For example:

If you want to print a float as a string, you need a conversion (float → string).

"{!s}".format(2.1457)
>>> 2.1457
type("{!s}".format(2.1457))
>>> <type 'str'>

If you don’t use the conversion mark, you will get an error.

"{:s}".format(2.1457)
>>> ValueError: Unknown format code 's' for object of type 'float'
Answered By: iFlo

!s, and its brethren !a and !r apply str(), ascii() and repr() respectively before interpolation and formatting. These are called conversion flags, and are part of the Format String Syntax spec, not the per-field formatting spec applied to values when interpolating:

The conversion field causes a type coercion before formatting. Normally, the job of formatting a value is done by the __format__() method of the value itself. However, in some cases it is desirable to force a type to be formatted as a string, overriding its own definition of formatting. By converting the value to a string before calling __format__(), the normal formatting logic is bypassed.

Bold emphasis mine.

:s only applies afterwards to the conversion result (or the original object if no conversion had been applied), and only if the __format__ method for the type of object supports that formatting option. Usually, only objects of type str support this formatter; it’s there as the default, mostly because the Format Specification Mini-Language allows for the existence of a type character and because the older % printf-style formatting had a %s format. If you tried to apply the s type to an object that doesn’t support it, you’d get an exception.

Use !s (or !a or !r) when you have an object that is not itself a string and either doesn’t support formatting otherwise (not all types do) or would format differently from their str(), ascii() or repr() conversions:

>>> class Foo:
...     def __str__(self):
...         return "Foo as a string"
...     def __repr__(self):
...         return "<Foo as repr, with åéæ some non-ASCII>"
...     def __format__(self, spec):
...         return "Foo formatted to {!r} spec".format(spec)
...
>>> print("""
... Different conversions applied:
... !s: {0!s:>60s}
... !r: {0!r:>60s}
... !a: {0!a:>60s}
... No conversions: {0:>50s}
... """.format(Foo()))
Different conversions applied:
!s:                                    Foo as a string
!r:             <Foo as repr, with åéæ some non-ASCII>
!a:    <Foo as repr, with xe5xe9xe6 some non-ASCII>
No conversions: Foo formatted to '>50s' spec

Note: all formatting specified by the format spec are the responsibility of the __format__ method; the last line does not apply the alignment operation in the >50s formatting spec, the Foo.__format__ method only used it as literal text in a formatting operation (using a !r conversion here).

For the converted values, on the other hand, the str.__format__ method is used and the output is aligned to the right in a 50 character wide field, padded with spaces on the left.

Answered By: Martijn Pieters

You’re unlucky you used strings as the value to be formatted. Using pretty much any other object you’d get how these aren’t the same.

In (as much as I can) layman’s terms:

  • The absence or existence of the conversion flag specifies the type of the value we going to format and, by extent, who’s __format__ we’ll be calling. As Martjin points out, by using this we can bypass certain behavior and treat the value more generically (like a string). It comes in three different flavors that correspond to the three different ways an object can choose to represent itself as a string.
  • The type specifier, coupled with other specifiers, specifies how the type we have should finally be presented. For strings there isn’t a rich set of options (a string is presented as is) but, for types like ints, you can have different presentations.

I do think, though, that type is probably a confusing name to give this specifier.