[Python-Dev] transitioning from % to {} formatting

Steven D'Aprano steve at pearwood.info
Wed Sep 30 16:34:27 CEST 2009

On Wed, 30 Sep 2009 03:04:05 pm James Y Knight wrote:

> It'd possibly be helpful if there were builtin objects which forced
> the format style to be either newstyle or oldstyle, independent of
> whether % or format was called on it.
> E.g.
> x = newstyle_formatstr("{} {} {}")
> x % (1,2,3) == x.format(1,2,3) == "1 2 3"

People will want this formatstr object to behave like strings, with 
concatenation, slicing, etc.:

>>> x = newstyle_formatstr("x{} {} : ")
>>> y = newstyle_formatstr("{}")
>>> (x[1:] + y) % (1, 2, 3)
'1 2 : 3'

Instead of having to support one type with %-formatting and 
{}-formatting (str), now the std lib will have two classes 
with %-formatting and {}-formatting. How is this an improvement?

Moving along, let's suppose the newstyle_formatstr is introduced. What's 
the intention then? Do we go through the std lib and replace every call 
to (say)

    somestring % args 


    newstyle_formatstr(somestring) % args 

instead? That seems terribly pointless to me -- it does nothing about 
getting rid of % but adds a layer of indirection which slows down the 
code. Things are no better if the replacement code is:


(or similar). If we can do that, then why not just go all the way and 
use this as the replacement instead?


> and perhaps, for symmetry:
> y = oldstyle_formatstr("%s %s %s")
> y.format(1,2,3) == x % (1,2,3) == "1 2 3"

Now we have three classes that support both % and {} formatting. Great.

> This could allow for a controlled switch towards the new format
> string format, with a long deprecation period for users to migrate:
> 1) introduce the above feature, and recommend in docs that people
> only ever use new-style format strings, wrapping the string in
> newstyle_formatstr() when necessary for passing to an API which uses
> % internally.

And how are people supposed to know what the API uses internally?

Personally, I think your chances of getting people to write:

logging.Formatter(newstyle_formatstr("%(asctime)s - %(name)s - %(level)s - %(msg)s"))

instead of 

logging.Formatter("%(asctime)s - %(name)s - %(level)s - %(msg)s")

is slim to none -- especially when the second call still works. You'd 
better off putting the call to newstyle_formatstr() inside 
logging.Formatter, and not even telling the users.

Instead of wrapping strings in a class that makes .__mod__() 
and .format() behave the same, at some cost on every call presumably, 
my preferred approach would be a converter function (perhaps taken from 
2to3?) which modified strings like "%(asctime)s" to "{asctime}". That 
cost gets paid *once*, rather than on every call.

(Obviously the details will need to be ironed out, and it will depend on 
the external API. If the external API depends on the caller using % 
explicitly, then this approach may not work.)

> 2) A long time later...deprecate str.__mod__; 

How long? I hope that's not until I'm dead and buried.

Steven D'Aprano

More information about the Python-Dev mailing list