[Python-Dev] summary of transitioning from % to {} formatting
Steven D'Aprano
steve at pearwood.info
Sat Oct 3 20:01:56 CEST 2009
On Sun, 4 Oct 2009 01:41:36 am Steven Bethard wrote:
> I thought it might be useful for those who don't have time to read a
> million posts to have a summary of what's happened in the formatting
> discussion.
>
> The basic problem is that many APIs in the standard library and
> elsewhere support only %-formatting and not {}-formatting, e.g.
> logging.Formatter accepts::
> logging.Formatter(fmt="%(asctime)s - %(name)s")
> but not::
> logging.Formatter(fmt="{asctime} - {name}")
Why is this a problem? Is it because the APIs require extra
functionality that only {} formatting can provide? Possibly, but I
doubt it -- I expect that the reason is:
(1) Some people would like to deprecate % formatting, and they can't
while the std lib uses % internally.
(2) Some APIs in the std lib are tightly coupled to their internal
implementation, and so you can't change their implementation without
changing the API as well.
Remove either of these issues, and the problem becomes a non-problem,
and no action is needed. Personally, I'd like to see no further talk
about deprecating % until at least Python 3.2, that is, the *earliest*
we would need to solve this issue would be 3.3. As the Zen
says, "Although never is often better than *right* now."
> There seems to be mostly agreement that these APIs should at least
> support both formatting styles, and a sizable group (including Guido)
> believe that %-formatting should eventually be phased out (e.g. by
> Python 4).
-1 on that. Time will tell if I change my mind in a couple of years, but
I suspect not -- for simple formatting, I far prefer %. Judging by the
reaction on comp.lang.python when this has been discussed in the past,
I think a large (or at least loud) proportion of Python programmers
agree with me.
> There are a number of competing proposals on how to allow
> such APIs to start accepting {}-format strings:
>
> * Add a parameter which declares the type of format string::
> logging.Formatter(fmt="{asctime} - {name}", format=BRACES)
> The API code would then switch between %-format and {}-format
> based on the value of that parameter. If %-formatting is to be
> deprecated, this could be done by first deprecating
> format=PERCENTS and requiring format=BRACES, and then changing the
> default to format=BRACES.
+0.5
> * Create string subclasses which convert % use to .format calls::
> __ = brace_fmt
> logging.Formatter(fmt=__("{asctime} - {name}"))
There are a few problems with this approach:
- Nobody has yet demonstrated that this brace_fmt class is even
possible. It should be easy to handle a restricted set of simple
templates (e.g. of the form "%(name)s" only), but what of the full
range of behaviour supported by % formatting?
- Even if it is doable, it is a wrapper class, which means the module
will suffer a performance hit on every call. At this time, we have no
idea what the magnitude of that hit will be, but .format() is already
slower than % so it will likely be significant.
- It strikes me as hideously ugly. I for one would delay using it as
long as possible, no matter how many DepreciationWarnings I got. I'd
drag my feet and avoid changing and complain loudly and then become
sullen and resentful when I couldn't avoid making the change. I'd much
rather go straight from %-based templates to {} in a single step than
have this Frankenstein monster intermediate.
[...]
> * Teach the API to accept callables as well as strings::
> logging.Formatter(fmt="{asctime} - {name}".format)
> The API code would just call the object with .format() style
> arguments if a callable was given instead of a string.
+0.5
> * Create translators between %-format and {}-format::
> assert to_braces("%(asctime)s") == "{asctime}"
> assert to_percents("{asctime}") == "%(asctime)s"
+1, assuming such translators are even possible. Being optimistic, such
translators would have one additional benefit: they would enable
modules to completely decouple the API they offer from their internal
implementation, without paying a runtime cost on every call, just a
single once-off translation at initialisation time.
In theory, this could mean that modules could, if they choose, continue
to offer an API based on % long after str.__mod__ is removed from the
language.
--
Steven D'Aprano
More information about the Python-Dev
mailing list