Deprecate str % format operator

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?

3 Likes

You’re a few days late, today is April 3rd…

For almost zero benefit, to boot. This is a Python-4-level change, and Python 4 isn’t happening.

edit: I’ll just add that I’d personally be fine with deprecating %-formatting but I just can’t imagine it being worth the effort.

14 Likes

Removing this will break my code and I expect a lot of other peoples code.

4 Likes

% formatting is quite handy when the string you’re formatting is full of literal {s and }s. I’d definitely miss it.

8 Likes

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.

2 Likes

Wouldn’t str.format postpone evaluation in the same way?

1 Like

%-style formatting is also far more familiar for people coming from older languages which use similar syntax (e.g. printf in libc).

On the other hand, it can be more of a security footgun for developers who aren’t careful to avoid operating on user-supplied format strings.

This is referring to code like logger.warning('Protocol problem: %s', 'connection reset'), where the resulting string isn’t created until it’s needed.

1 Like

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.

6 Likes

Sorry for misunderstanding. You’re correct, you could do the same thing with str.format.

1 Like

logging.Formatter supports a style="{" option. It makes logging use str.format for merging the data.

4 Likes

No. Lets not.

3 Likes

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.

2 Likes

Also, a lot of people come to python from languages where the %s and %.2f styles are familiar.

1 Like

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 :


# Python String Formatting with `%` Operator

## Full List of Format Symbols

| Symbol | Description            | Example          |
|--------|------------------------|------------------|
| `%s`   | String conversion      | `"%-10s" % "Hello"`
| `%d`   | Signed decimal integer | `"%5d" % 42`     |
| `%i`   | Same as `%d`           | `"%+5i" % -42`   |
| `%u`   | Unsigned decimal       | `"%5u" % 42`     |
| `%o`   | Octal integer          | `"%#5o" % 8`     |
| `%x`   | Hexadecimal (lowercase) | `"%#6x" % 255`   |
| `%X`   | Hexadecimal (uppercase) | `"%#6X" % 255`   |
| `%f`   | Floating-point         | `"%.2f" % 3.14159`
| `%e`   | Scientific notation    | `"%10.2e" % 12345`
| `%E`   | Scientific notation    | `"%10.2E" % 12345`
| `%g`   | Shorter of `%f`, `%e`  | `"%.2g" % 12345`  |
| `%G`   | Shorter of `%f`, `%E`  | `"%.2G" % 12345`  |
| `%%`   | Literal '%'            | `"%%" % ""`      |

---

## Additional Notes

- **Width and Precision**: Specify minimum field width and precision.
  - Example: `"%10.2f" % 3.14159"` for floats.

- **Special Flags**:
  - **Left alignment**: Use `-`.
    - Example: `"%-10s" % "Hi"`.
  - **Zero-padding**: Use `0`.
    - Example: `"%05d" % 42`.
  - **Sign display**: Use `+`.
    - Example: `"%+d" % 42`.


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 know it’s old fashioned, but you could have looked in the python docs…

I looked for % in the index (under Symbols) that took me here Built-in Types — Python 3.13.2 documentation

4 Likes

True, I failed to find it.
Btw I read there that the correct name is “printf-style String Formatting”, I should have typed that in a search engine.

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.

3 Likes