th 2 days ago

I created a tool over the weekend to guess the f-string you seek: http://pym.dev/format

I'm also the author of https://fstring.help/cheat/ (though not of the homepage) and I haven't yet linked back to that new tool. I was surprised to the cheat sheet here today but not the format guesser.

  • lbourdages 2 days ago

    Is it at all possible to use spaces as the thousands separator in Python f strings?

    At the very least, your tool wasn't able to figure it out and hardcoded most of the number I wrote in.

nickcw 3 days ago

From the f-strings PEP 498

https://peps.python.org/pep-0498/

> This PEP is driven by the desire to have a simpler way to format strings in Python.

I think f-strings have become the thing they were trying to replace and they are now more complicated than the old % interpolation.

I still like f-strings and use them a lot but if you look at the cheat sheet, they are no longer simple.

  • ayhanfuat 3 days ago

    > if you look at the cheat sheet, they are no longer simple.

    Most of the formatting options in the cheat sheet have always been available with f-strings. They are part of the Format Specification Mini Language (https://docs.python.org/3/library/string.html#format-specifi...) and both string.format and f-strings use the same spec. f-strings have some additional capabilities like inline expressions and debugging/logging with `=`.

    • disgruntledphd2 2 days ago

      The equals addition is basically the best thing ever, I almost always use it for logging. Incredibly helpful (for me, at least).

  • lynnharry 2 days ago

    I don't think "simple" here means lack of functions. It means more intuitive and simpler code, and easier curve of learning. And to me f-string is very simple.

  • zahlman 2 days ago

    "Simpler" here is at least partly comparing to explicit calls to the .format method, which was added all the way back in 2.6.

    %-style interpolation supports many of these features, they just weren't as well known or discussed back then. The % style is also more complicated because of the weird edge cases (like trying to interpolate a single value which is a tuple).

  • 3abiton 2 days ago

    I think complexity is a byproduct of flexibility. At least in this case, there is a beginner version.

ilovetux 2 days ago

I love python f-strings. I dont use the format specifiers that this article points out.

Also, even though use in log messages is discouraged, I go ahead and use them. It will let me know if there is some code path where the proper variable is never set. This usually comes out through testing, especially during fuzzing so I guess it really only works because of my testing, otherwise it would come up during runtime...

jonathaneunice 2 days ago

I don't understand the f-string hate.

f-strings put the value in the output string exactly where the value should be. Massive win for contextual awareness, no need to count ...3, 4, okay what's the position 4 value over on the right, does it match up?? And they use classic Python string formatting commands, except the = operator which makes them even better with a "name the variable, show its value in a concise way" option. What's not to like?

(And if you don't like them, uh...they're not mandatory. Just don't use them.)

  • somat 2 days ago

    If i had to guess, the implicit access to all your vars feels gross to some, it's why I don't use them. I mean they are probably fine, like you said, puts the symbol right where you want it. It just never felt right to me. so I keep up the double dance of explicate format() calls.

    It is probably some sort of deep seated printf() based trauma.

    • danwills 2 days ago

      I recently swapped from adding strings together (just what I've always done) to f-strings and I'm not looking back!

      I even get to keep the idea of being able to read the vars in-place in the string which is certainly the last thing that I needed to be happy to use them! Full convert now!

roenxi 3 days ago

This looks like a cheatsheet for writing a hard-to-read Python script. I don't know who gets karmic brownie points for a f"{string:>20}" field, but under most normal cases it'd be better to use rjust() directly and not force people to remember Yet Another DSL.

Once a reader could be reasonably expected to consult reference material while working out what a print() is doing something has gone wrong. This is the programmer equivalent of wearing too much makeup.

  • cloudbonsai 3 days ago

    You haven't seen the full depth yet. Suppose that you encountered with this line:

        print(f"{n:.2g}")
    
    What will it print? Here is the official explanation from https://docs.python.org/3.12/library/string.html#formatspec:

        g - General format. For a given precision p >= 1, this rounds the number to p significant digits and then formats the result in either fixed-point format or in scientific notation, depending on its magnitude. A precision of 0 is treated as equivalent to a precision of 1.
    
        The precise rules are as follows: suppose that the result formatted with presentation type 'e' and precision p-1 would have exponent exp. Then, if m <= exp < p, where m is -4 for floats and -6 for Decimals, the number is formatted with presentation type 'f' and precision p-1-exp. Otherwise, the number is formatted with presentation type 'e' and precision p-1. In both cases insignificant trailing zeros are removed from the significand, and the decimal point is also removed if there are no remaining digits following it, unless the '#' option is used.
    
        With no precision given, uses a precision of 6 significant digits for float. For Decimal, the coefficient of the result is formed from the coefficient digits of the value; scientific notation is used for values smaller than 1e-6 in absolute value and values where the place value of the least significant digit is larger than 1, and fixed-point notation is used otherwise.
    
        Positive and negative infinity, positive and negative zero, and nans, are formatted as inf, -inf, 0, -0 and nan respectively, regardless of the precision.
    
    Make sense? You now should be able to see why it's called f-string.
    • jgtrosh 3 days ago

      Yes, it's a generalisation of `%g` in f-string's ancestor printf(3). This is what people expect to find in formatting templates.

    • zahlman 2 days ago

      I don't know why people look at paragraphs of documentation that explain the exact results in strange edge cases (which have to exist because of the underlying complexity; in this example, Python can't change how IEEE-754 works, nor the original C printf specifier they're emulating) and conclude that this proves some strange and unexpected complexity was introduced.

      When documentation isn't this thorough, people complain about that, too.

    • cwilkes 2 days ago

      Maybe should be called “iq-string” for Interview Question string.

xavdid 3 days ago

Ah, these are great! f-strings are so powerful, but I can never remember the arcane little syntax. Definitely bookmarking this.

nomel 2 days ago

And, don't forget you can pass everything after the ":" to a the `__format__(self, spec: str)` method, with its neat use cases, like unit conversion.

lenkite 2 days ago

Say if you were designing a new language and wanted to include string formatting as a feature. Would you personally choose Python f-strings or C-style format strings or Rust-style formats ?

  • hmry 2 days ago

    In terms of format string syntax, Rust is extremely similar to Python (and C++'s new std::format is also similar). So in that sense it seems Python-style "{name:fmt}" has won. Though the others don't support arbitrary expressions inside of the string.

    On the other hand, you have formatting as an expression that evaluates to a string like f"..." in Python, vs formatting as a set of functions that take the same input format but do various things like printf/snprintf/write!/format!/std::print/std::format... Here it seems Python's approach had too many drawbacks, considering they just added formatted templates t"..." that don't evaluate to a string.

    If I were to design a new language, I would use a Python-like "{expr:fmt}" syntax for formatting, but I would make it evaluate to some sort of FormatTemplate object that code can inspect.

    • f33d5173 2 days ago

      > Here it seems Python's approach had too many drawbacks, considering they just added formatted templates t"..." that don't evaluate to a string.

      That's not a fair characterization at all, since the plan was always to add something like the t"" strings. Having a version that immediately evaluates to a string is convenient, and adds very little complexity either at the implementation level or conceptually.

      • zahlman 2 days ago

        I don't think that was always the plan. Certainly the proposal has been tossed around for a while, but the overall historical evidence paints a different picture for me.

  • pansa2 2 days ago

    Can you get away with just f-strings? Python also has `string.format` for situations where the string needs to be built dynamically.

    Are there languages which only have an f-string-like feature without a `string.format` equivalent?

  • zahlman 2 days ago

    t-strings with str.format-like limitations on what can be substituted in, except the t is actually an operator that applies at compile time when its argument is a literal and at runtime otherwise. And then f is another such operator that sugars over a standard library format(t '{...}') call.

    (I've been thinking about this for a while, actually. Since PEP 751 was still in discussion.)

jokoon 2 days ago

I use them and I discovered a few that I did not know

TZubiri 2 days ago

Here's my cheat sheet:

"STRING" + str(var) + "STRING"

  • Sohcahtoa82 a day ago

    I find "STRING {var} STRING" much more readable. Also, far easier to type. You don't even need to explicitly convert to a string first, it happens implicitly (Which, ironically, goes against the "Explicit is better than implicit" line in the Zen of Python).

    But this page isn't about merely inserting a number/string into a string, it's about expressing how you want that number/string formatted. Maybe you only want the first two digits after a decimal. Maybe you want it to always be the same width and to pad with spaces or zeroes. Or any other ways of formatting.

  • metalliqaz 2 days ago

    Or even print("STRING", var, "STRING")

    keep in mind that for long strings, each `+` creates a new string object (at least, it did in the 2.x days and I assume it hasn't changed)

    • zahlman 2 days ago

      It has indeed not changed. But practically speaking it doesn't matter much. The string implementation cheats by using resizable buffers internally for at least some purposes, while presenting an immutable-type interface. But regardless, a given line of code is going to have O(1) such additions; it's not remotely as bad as `for i in items: str += foo(i)`. (This should be done using `''.join` instead.)

    • TZubiri 2 days ago

      #Takes around 1 second s="a"*3000000000

      #instant, does not consume twice as much memory. s+="a"

      I don't know the internals, but certainly there's not a new string being created. Maybe if it exceeds capacity? Who cares at that point, it's python strings, not Matmuls in C.

blueflow 3 days ago

I fail to see the purpose of f-strings if they end up as complex as printf formatting. Maybe use printf at this point?

  • ForceBru 2 days ago

    I think the purpose is to put variables directly in the string literal instead of calling `printf`. Looking at an f-string immediately tells you where each value will be located in the output. `printf` requires you to read the format string, then look at the arguments, then count where a particular value will be printed.

    • lou1306 2 days ago

      `printf`/`str.format` are also prone to nasty failures: if you forget a variable (e.g., `str.format("{} {} {}", 0, 1)`), you only find out when you crash with an IndexError at runtime.

      • zahlman 2 days ago

        I would expect that linters can pick up this sort of thing pretty easily.

  • Hamuko 3 days ago

    You don't really need to use any of these. Really the most useful one is f"{var=}" for us print() debuggers. But f"{var:#x}" is the same as f"{hex(var)}", so feel free to pick whatever you prefer.

  • positr0n a day ago

    Sometimes you need a formatted string outside of a printf content.