My problem statement in favor of this is that I’m implementing a math library that uses str literals in expressions, and x % "lit" works, but "lit" % x does NOT work b/c str implements the % operator. x is an instance of a custom numeric type. See bvwx/src/bvwx/_bits.py at main · cjdrake/bvwx · GitHub for the specific line of code where I ran into this limitation.
I will note, however, that "lit" * x DOES work. I suspect that’s b/c a deep Python protocol thing that I don’t understand returns NotImplemented if x is not an int.
A more generic argument in favor is that the official documentation (string — Common string operations — Python 3.13.2 documentation) calls it “the old % -formatting” method, and it has been supplanted by superior methods for string formatting (f-strings and str.format).
The obvious argument against is backwards compatibility. Saying this change would affect lots of old code would be an understatement.
Aside from my personal issue, the reason I’m bringing it up is that I was surprised I couldn’t find much discussion about this topic. I suspect most people want this to happen, but never prioritize it due to its large cost. Is there another discussion forum where this has already been hashed out?
This method is still encouraged in log messages (by pylint at least). The interpolation can be thus postponed till the message successfully passes all filters. So it is “old”, but not “history”.
A deprecation is out of question, but nevertheless it would be nice to have a modern way of postponed interpolation as much similar to str.format or f-strings as possible.
Yeah, I get that. As I understand logging.py, that log eventually gets turned into something like self._fmt % values where values would be ('connection reset',) in this case.
My point was that this (and similar uses of % in the logging code) could be spelled self._fmt.format(values) and it should have the same delayed-string-construction properties that logging currently has. edit: this exists already
It would be a lot of needless churn to rewrite logging to make that work, but I was just trying point out that the % operator isn’t special in its ability to format a string with arguments that are supplied later.
But maybe I misunderstand some aspect about how this works, and str.format can’t do the same thing. That’s why I was asking.
Thank you for the information. Pylint emits just: “Use lazy % formatting in logging functions”.
Now I see it is not the only correct option. OTOH a custom formatter is required for alternative styles. The usual logger=logging.getLogger(__name__) at the top of a module is not enough. I guess the %-style will further dominate in logging.
IMO %-style formatting is the easiest and clearest way to format bytes for low-level protocols. With f-strings you end up with too much code in the (byte)string.
I haven’t been using it for long, but I think these old format-specifiers do not have a fully viable replacement option, especially regarding the (old way of) interactive prompting through the terminal.
Btw, I just worked with an LLM to rebuild the full cheatsheet of these “%-format-specifiers” usage, heredown :
Amazing, I wasn’t aware logging formatters supported format syntax ({}).
Even though I don’t particularly like %-formatting, I still use it in logging (though see note above), and in regular expressions. When building regexps, the r (raw) prefix is invaluable, as it helps editors highlight the expression accordingly, as well as remove the need to escape \. Yet if you also add the f prefix (f-strings), some editors will stop highlighting as a regular expression (VSCode). And in both cases of f-strings or {}-formatted strings, you have to escape { and } by doubling them, while these characters can appear quite often in regexps. In the end, the {} syntax is very inconvenient in regexps, making %-formatting the only viable solution.
I want to clarify. I am suggesting we deprecate the %operator for string formatting. I am NOT suggesting we get rid of printf-style formatting.
For example, you could change "%s %s" % ("foo", "bar") to "%s %s".oldfmt("foo", "bar"), and that’s perfectly fine. People can continue using it for whatever purpose makes sense.
I understand it doesn’t change the cost much, but it’s an important distinction.
The % operator is used to calculate remainders in C and Python. It’s a math operator. Some math operators like + and * have obvious meaning for strings. New users understand the concepts of adding (concatenating), and multiplying (repeating) strings. Taking the remainder of a string, however, doesn’t have a natural analogue. Strings don’t support the / and // operators either.