18 ms·
Python 3's F-Strings: An Improved String Formatting Syntax
- noncoml 6y agoI can't say I like the f" syntax much. I think JS's backtick is better. Other than that, I agree, much better than % and less verbose than .format. It's just the f" or f' that doesn't sit right with me
- dharmab 6y agoIt's consistent with other strings in Python like r-strings and the older u-strings.
- teddyh 6y agoAnd b-strings.
- nurpax 6y agoYup, especially as you often need to access dict's with foo["field"] and you get a syntax error if the f-strings is also f"". JavaScript backtick syntax doesn't lead to this extra syntactic friction.
- quietbritishjim 6y agoYou can use any of f"{foo['field']}" f'{foo["field"]}' f"""{foo["field"]}""" (I see from your carefully worded first sentence that you already knew this but still find it annoying, but I'll leave this here for any that aren't aware.)
- mixmastamyk 6y agoYes, and anyone who has written a "one-liner" at the command line is familiar with it. Wouldn't say it is a big problem.
- psalminen 6y agoThe problem with this is when you run it through a formatter that changes all ' to ".
- dmurray 6y agoSounds like a bug in your formatter, if it's meant to just do formatting but actually replaces valid syntax with invalid.
- psalminen 6y agoI don't totally disagree. My company uses the black formatter, which does this[0]. There are flags to skip string formatting, but is frowned upon at my organization. [0] https://github.com/psf/black/blob/master/docs/the_black_code_style.md#strings https://github.com/psf/black/blob/master/docs/the_black_code...
- nimih 6y agoFor what it's worth, black appears to do a reasonable thing and preserves the semantics of your quoted strings (as is promised by the documentation): $ echo "'foo'" | black - "foo" $ echo "'foo[\"bar\"]'" | black - 'foo["bar"]' $ echo "'foo[\"bar\"]+\\'baz\\''" | black - "foo[\"bar\"]+'baz'"
- wskinner 6y agoThis is easily avoidable in python by using single quotes for the outer string and double quotes for the inner string, or vice versa. Or, use triple quotes for the outer string.
- dragonwriter 6y agoYeah, if you get to the level of nesting where having available quotes is an issue, you really need to break it up into multiple expressions.
- ziml77 6y agoI'm surprised it works like that in Python. C# has similar interpolated strings but there it's legal to embed quotation marks within the curly brackets. Though I guess it's less necessary in Python since you can just use the other type of quote inside.
- ericvsmith 6y agoThis is because of how f-strings were initially implemented: I piggy-backed them off of "regular" strings, then post-parsed inside that string. But the restriction is that the entire f-string (after the f) needs to be a valid regular Python string. Since this: "this is a "regular" string" isn't valid, then neither is this: f"dict lookup: {d["key"]}" However, we've talked about changing f-string parsing from a "post-process regular strings" mode, to instead having the tokenizer and parser themselves aware of f-strings. When we do that, the f-string example above will work. (edit for formatting)
- foob 6y agoIt can feel annoying sometimes, but it's also nice that it prevents you from getting overly complicated with your string interpolations. Human readability is highly valued in Python culture, and things start getting hard for humans to quickly parse when you allow arbitrarily complex interpolation expressions. Assigning your complicated expression to a well-named variable first will usually make your code more readable, even if it's slightly more verbose.
- mixmastamyk 6y agoI agree it's not the most beautiful, but it was the best choice given the requirements. Basically, it had to be explicit, concise, use unused ASCII syntax, and not be too cryptic or incompatible. Other quote types or backticks were not available or an option. (I spent a lot of time thinking about it, and put the idea forward on python-ideas. Though string interpolation is nothing new of course.)
- patrec 6y agoI think you are right to prefer JS's backtick notation, but not in your reason. The main problem with python's f" syntax is that it's just a very complex, non-extensible hack whereas in javascript you can prefix a tag to specify how the interpolation happens. Instead of this, f-strings have a lot of hardcoded and often broken behavior which can't be fixed let alone customized. For example, you can specify a field width, but it will not even work properly even in a fixed-width context, because of quite common things like emojis or CJK or combining characters don't have their width computed correctly.
- mixmastamyk 6y agoIt's not a hack or broken. The formatting language existed before f-strings and is somewhat a separate issue. I'm not sure I expect it to handle every Unicode nuance either, when there are modules in the stdlib for that.
- earthboundkid 6y agoYes. Python in general has gone from being “executable pseudo code” to a series of complex language design trade offs largely because it had failed to grasp the right abstractions in its evolution. Backtick functions in JS are so powerfully clever and basically impossible to recreate in Python.
- pansa2 6y ago> Python in general has gone from being “executable pseudo code” to a series of complex language design trade offs Agree with this 100%. > because it had failed to grasp the right abstractions in its evolution. I'd be interested to know what you think the language's designers should have done differently?
- earthboundkid 6y agoLet’s go all the way back to `with`. What does it do? It encapsulates try/finally blocks and only try/finally. That is a weirdly specific thing to encapsulate. Why did they do that? Well, it turns out that making an anonymous block syntax that is compatible with Python’s grammar is hard, so you can’t have the equivalent of this JavaScript: WithOpen(filename, f => { // block }) Because blocks aren’t viable, problems have to be solved on a case-by-case basis, which is hard to do well and leaves gaps.
- ericvsmith 6y agoI've put together a proposal to have all strings be f-strings by default. Then you could drop the "f" prefix. I also propose adding a "p" prefix (for "plain") to remove the f-string parsing behavior. I talked about it at the last Python core sprint, and it only got lukewarm support, so I'm not sure I'm going to pursue it.
- cdrini 6y agoI would personally be quite against that. That's a pretty large breaking change, with no clear benefit. It doesn't make the feature more discoverable, since you still need to know that using "{}" will cause interpolation. I'd say net zero effect on readability; mostly just individual preference. And potential negative impact on performance.
- ericvsmith 6y agoThere would be a __future__ import to turn it on, at least for some number of releases. But yeah, I'm not sure it should ever be the default behavior. It makes most sense to me as a per-module opt-in, but then a __future__ import doesn't make sense. And we've resisted having per-module pragmas, so maybe it's dead in the water.
- boothby 6y ago> And we've resisted having per-module pragmas, so maybe it's dead in the water. There's a hack for that (no apologies for the self promotion; the project is a just funnin') https://github.com/boothby/dissert https://github.com/boothby/dissert
- Blikkentrekker 6y agoCertainly this will lead to rather confused programmers. F-strings certainly seem like opt-in behavior to me.
- dragonwriter 6y ago> I've put together a proposal to have all strings be f-strings by default. Please, no. I mean, it would be a breaking change, which needs a really compelling case, will make all introductory material wrong, and...offers what benefit?
- pansa2 6y agoI quite like the idea of using double-quotes for format strings and single-quotes for normal strings. This isn’t sufficient on its own, though - what about raw strings? Perhaps use “” for format strings, ‘’ for raw strings and get rid of normal strings altogether?
- driverdan 6y agoF-strings have been around for years and this article is at least a few years old. I'm curious why this is coming up now.
- EForEndeavour 6y agoI wonder if the submitter was inspired by this tweet that went viral (by python standards) yesterday: https://twitter.com/mariatta/status/1351359518316216321 https://twitter.com/mariatta/status/1351359518316216321
- mixmastamyk 6y agoCapital f-strings should have not been allowed in my opinion.
- t-writescode 6y agoPeople finally forced to move off Python 2.7?
- abdusco 6y agoNot mentioned in the article, but with Python 3.8, f-strings have gained the ability to print debug information using `=` specifier. name = 'world' print(f'hello {name.upper()=}') outputs: hello name.upper()='WORLD' It's small, but very useful in debugging an issue or logging something to the console. https://docs.python.org/3/whatsnew/3.8.html#f-strings-support-for-self-documenting-expressions-and-debugging https://docs.python.org/3/whatsnew/3.8.html#f-strings-suppor...
- cabalamat 6y agoThat's something I didn't know, but will know be using.
- richardARPANET 6y agoWay better -> https://pypi.org/project/q/ https://pypi.org/project/q/
- Denvercoder9 6y agoThe fact that it can only output to a single hardcoded file already proves that it is not way better in the general case.
- deleted 6y ago[deleted]
- ipiz0618 6y agoThis is neat! I can think of several instances where this would be useful in my code
- timeoperator 6y agoPHP: Am I joke to you?
- noncoml 6y ago> PHP: Am I joke to you? JFYI meme style responses are frowned upon at HN. That's why you get downvoted. A much better rephrase of the above would be something along the lines of: PHP/Javascript/.. also have this feature since ver X. It's main difference/advantage/disadvantage is ...
- timeoperator 6y agoThanks.
- brian_herman 6y agoHere is a more in-depth tutorial. https://zetcode.com/python/fstring/ https://zetcode.com/python/fstring/
- rdtsc 6y ago> However, once you start using several parameters and longer strings, your code will quickly become much less easily readable [...] `"Hello, %s %s. You are %s. You are a %s ...` Minor correction: % formatting allows specifying names for dictionary keys too: In [1]: "%(a)s and %(b)s" % {"a":"A", "b":"B"} Out[1]: 'A and B' In fact I had never switched to using str.format as it just never added enough benefit for me over the old %-based formatting. But I do like f-strings and am using them quite a bit. Calling functions or methods inside f-strings is something I had no idea was possible. That is pretty neat: >>> name = "Eric Idle" >>> f"{to_lowercase(name)} is funny." 'eric idle is funny.'
- mixmastamyk 6y agof"{name.lower()} is funny."
- rdtsc 6y agoThe article demonstrates both methods and functions. I just pasted the function example, but without the showing the function definition itself as it's fairly obvious :-)
- climb_stealth 6y agoI have mixed feelings about code inside f-strings. It seems like a bit of a slippery slope towards losing track of what's happening. I have been tempted to write things like below because I really just need it for the print. f"Info print for item {hex(do_some_parsing(do_some_conversion(value)))}" Which runs perfectly fine, but at least on my editor syntax highlighting stops working as it is all part of the string. And with that it becomes increasingly hard to work out what is actually being called. Now I resolved myself to pretty much just have one function call and no nesting. Things like f"{str.lower()}" or f"{hex(value)}" are great and I use them a lot.
- hajile 6y agoI don’t think it’ll be an issue. This isn’t exactly new ground. We know the results from other languages. There might be some such abuse, but there’s a lot of other ways to abuse expressions and they don’t happen often because it’s just not worth it.
- gh123man 6y agoThis reminds me of Swift's string formatting syntax: let name = "Bob" print("Hi my name is \(name)") Which I always quite liked. I found myself missing this when I moved on to Rust and Go.
- ausbah 6y agoKotlin is even simpler with statements like "hello $variable" or "hello ${function()}"
- blackrock 6y agoThis overloads the backslash \(). Which is usually used for escaping characters. Like, \n for new line. So, you may end up with code like: print(“Hi. \nMy name is \(name).”)
- Someone 6y agoAnd do you think that’s good or bad? I think it’s nice that there is only one escape character. You check for ‘\’. If it’s there, switch on the next character checking for ‘n’, ‘t’, ‘r’ or ‘(‘, defaulting to appending the unmodified character to the output. I think that might not only be simpler, but also be slightly faster than having having two different escapes would be.
- saagarjha 6y agoI think the intent here is that you are escaping into an interpolation context…
- ibraheemdev 6y agoThe implicit formatting arguments RFC has been merged [0], so you will be able to write this in Rust soon: let name = "Bob"; print!("Hello {name}"); [0]: https://rust-lang.github.io/rfcs/2795-format-args-implicit-identifiers.html https://rust-lang.github.io/rfcs/2795-format-args-implicit-i...
- kzrdude 6y ago
- kazinator 6y agoA couple of years ago I wrote some Python code which took advantage of this syntax. Then it turned it had to run in an environment without it. I wrote a simple Awk script to convert Python with f-string syntax to syntax without it. (The program had a function to do a more or less proper lexical analysis job; not some flimsy regex substitution hack.)
- renewiltord 6y agoShould you ever find yourself in a situation like that, https://github.com/python-rope/rope https://github.com/python-rope/rope might help.
- CJefferson 6y agoThe thing which annoys me most about f strings is I can't build them at runtime, which means I can't just learn/teach f strings. While I know why building f strings is forbidden (they can run arbitrary code), I'm happy to take the risk.
- wodenokoto 6y agoDoesn't calling .format on a string do the exact same thing as an f-string?
- CJefferson 6y agoNo, because f strings can run arbitrary python code, you can write f"{X} - {y} = {X - y}"
- throwaway894345 6y agoI mean it's Python, so you can do anything: >>> fstring="Hello, {\"world\"}" >>> eval(f"print(f'{fstring}')") Hello, world
- CJefferson 6y agoThat doesn't work (easily) in a function as eval only has the global context, not access to local variables (although you can do some hackery to make it work)
- epage 6y agoDelayed formatting was brought up in the PEP but was rejected. That made me a little sad.
- bjoli 6y agoWhen it comes to string formatting I have recently found my absolute favourite utility of all time: Scheme's SRFI-166. It is a combinator based string formatting utility that is user extensible (with verbosity worthy of being in every scheme!): https://srfi.schemers.org/srfi-166/srfi-166.html https://srfi.schemers.org/srfi-166/srfi-166.html
- eachro 6y agoThis is super minor but I really wish f-string formatting were the default behavior for `print` or that there were a `printf` command just for it. Typing that extra f character before the quotes is just annoying enough
- musicale 6y agoAnd while they're at it, bring back the print statement. It was a beautiful thing and made it possible to write the same "hello world" program in Python and BASIC: print "hello, world"
- ghshephard 6y agoAs a novice Python scripter, I felt that way for about 6 months. Then grumbled but accepted why making print a function was a good thing. Then spent another year frequently forgetting the brackets. Then another 6 months occasionally forgetting the brackets. Now, about 2 years after I converted to Python3, I finally have fully accepted that having print as a function is a good universal thing. At the very least, having an "end=" and "sep=" option eliminated 10,000 newbie python questions a year.
- DylanDmitri 6y agoYeah it's a backwards compatibility issue, wouldn't be surprised if they make this change in Python4
- pansa2 6y agoMaking f-strings the default was discussed at the Python Language Summit last year: https://pyfound.blogspot.com/2020/04/all-strings-become-f-strings-python.html https://pyfound.blogspot.com/2020/04/all-strings-become-f-st...
- kzrdude 6y agoI just find the convergent evolution funny, that my C fingers that could type `printf()` fluently now learn to type `print(f"")` the same way
- psychoslave 6y agoSo far Ruby provided me the best user experience regarding string interpolation, and it owes a large part of it to Perl. Squiggly heredoc is something I am unaware of in any other general programming language.
- dnautics 6y agoElixir heredocs do this by default. Plus you can prepend it with sigils to activate string macros. Here I use it to inject content into another programming language embedded within Elixir (lower case ~z allows interpolation, upper case ~Z does not): https://www.youtube.com/watch?v=fIxNEILcNGM&t=25m50s https://www.youtube.com/watch?v=fIxNEILcNGM&t=25m50s
- japhyr 6y agoI learned this nice syntax from a tweet yesterday: >>> import datetime >>> now = datetime.datetime.now() >>> f"Today is {now:%m/%d/%Y}." 'Today is 01/20/2021.' No need for now.strftime() in simple string output! https://twitter.com/mariatta/status/1351359518316216321 https://twitter.com/mariatta/status/1351359518316216321
- CGamesPlay 6y agoThank you! This was literally what I opened the article for (I'd seen the f syntax previously but hadn't paid much attention) and it's conspicuously missing!
- wrigby 6y agoI love this - didn't realize it was a thing at all! If anyone is curious, this works for any type in Python that implements __format__ ({now:%m/%d/%Y} is equivalent to {now.__format__('%m/%d/%Y')}), and you can (ostensibly) make the format string mean whatever you want.
- gitanovic 6y agoWhile I really do like this, I cannot avoid remarking that python starts to look more and more like perl
- dec0dedab0de 6y agoI found out about f strings from an HN comment a few years ago. Ever since I have been super excited to show people who don't know about them. In Python 2 I used to use .format(**locals()) which is basically the same thing, but felt like a hack.
- ericvsmith 6y agoThe more efficient version of this is .format_map(locals())
- dec0dedab0de 6y agoI didnt know format_map existed, but it looks like it is not in python 2 so it wouldn't have helped in that use case. Still, thanks for pointing that out. I love learning about new minor conveniences in Python.
- pfranz 6y agoI saw this a few years ago and thought it was cool at first, but I had huge problems when refactoring that code. I would miss variables used in strings when I would rename, move, or delete things. Have you had this issue? I imagine an IDE that could grok it would have helped.
- zo1 6y agoPycharm "refactor" for variables has the option to search for the variable-name being changed in comments and strings. But for f-strings, it has full support for the "inline" variables.
- mbeex 6y ago> But for f-strings, it has full support for the "inline" variables. Is this complete (meaning: it also includes more complicated expressions, referencing these variables)? I'm asking, because some Python development tools are failing in doing it right. This leads to annoying reliability issues for refactorings (even for simple renames). Currently, I have the problem with Wing IDE.
- theptip 6y agoF-strings are great. The only downside I’ve found is they are incompatible with the stdlib logging framework’s deferred string interpolation feature (using %s style string interpolation), which can give you a nice performance boost if you make lots of expensive debug logs for tests, but run at info level in production. That said most apps probably won’t see much of a difference in perf between the two approaches.
- ericvsmith 6y agof-strings are so much faster that I've seen an increase in speed over %-formatting for logging. If there's even a 10% chance that a string will be actually logged (and therefore %-formatting will take place), I'll replace it with an f-string and not care about the deferred formatting.
- pfranz 6y agoHere are some benchmarks and a discussion about using f-strings in logging I remember reading a few years ago: https://github.com/PyCQA/pylint/issues/2395 https://github.com/PyCQA/pylint/issues/2395
- heisenzombie 6y agoSomething I find useful, and perhaps isn't widely known is that you can pop curly braces into the format part of an f-string to have dynamic formatting: value = 5/6 precision = 2 print(f"{value:.{precision}f}") > 0.83
- noobermin 6y agoThis is great, had been hacking around this with formatfor years. Will incorporate this soon.
- btilly 6y agoI have a love-hate relationship with f-strings. They are nice to use. But they are also the single feature that I'm most likely to use that means I can't use pypy to run my code many times faster. I wish that pypy supported them.
- korethr 6y agoUpon reading the title, I was not enthusiastic. Yet another way to format strings? What's wrong with String.format()? Get off my lawn! However upon reading the article, it does feel more ergonomic to prepend your string literal with an 'f', and have it automagically reach out into the surrounding scope for the variables to format into the string. I do worry a bit however about formatting not just expressions, but being able to call other code. Yes, that could be quite convenient for keeping the code concise and avoiding the kind of verbosity that confuses. However, I could also see someone forgetting to sanitize user-input data, and combined with the f-string, creating a remote arbitrary code-execution vulnerability. Sure, an attacker can't immediately start scribbling on memory in Python, there's more work he has to do first. But, he possibly could hijack the Python process to run whatever Python code he wanted.
- ericvsmith 6y agoExecuting user-provided code isn't possible. f-strings must be literals, so there's no user-defined strings involved. You could of course eval something the user provided, but there's no more of a risk with f-strings than there is with regular code. This is addressed in PEP 498.
- lightlyused 6y agoI love f-strings. Don't forget that you can do fr"some string {blah}" to build raw strings. >>> blah = "something \here" >>> fr"what the {blah}" 'what the something \\here'
- ghshephard 6y agoWhat version of python are you using. With python 3.8.5 1. \h doesn't get interpolated to anything (unlike \a, \b, \f, etc...) "\h" '\\h' "\a" '\x07' 2. The "r" needs to be in the original definition, not the f-string. blah = r"\a" You only need the "r" in the f-string if you don't want interpoolation of the "\" - I.e.: fr" this\a {blah}" 'This\\a \\a
- lightlyused 6y agoThis was just an example to show what it does, I normally wouldn't have \h in a string. I use this to store lists of regular expressions in aws dynamodb list type and to convert them back to raw strings when used.
- stubish 6y agoA wart is the standard logging module, which uses %-style strings. For example: log.debug("Unexpected, got %r", (got,)) By doing it this way, it can defer rendering the string until it is actually needed, which is (assumed to be) a win when the logging module is normally just going to toss the low level messages away rather than emit them anywhere. I think you lose this optimization if you use f-strings. I don't think rendering of the string can be lazy, deferred until actually needed.
- shakna 6y agoWhilst percent strings can't, calls to format can. So: log.debug("Unexpected, got {got}".format(got=got)) Or, to just use the available locals to automatically fill that in for you: log.debug("Unexepected, got {got}".format(**locals())) Or if you want both globals and locals with a preference for locals: log.debug("Unexepected, got {got}".format(**{**globals(), **locals()}))
- jon_richards 6y agoYes, I was asked by a Junior if they should always use f strings now. There are still a couple situations where you need format, such as when you’re formatting a variable instead of a literal. Also, shoutout to Template for untrusted strings. It’s surprisingly unknown. Most people suggest replace or a custom format function.
- ploxiln 6y agoAlso, remember that the first argument to the logging functions is considered to be a format string. So using f-strings is not just less efficient, it is also not robust, unless you do: logging.debug("%s", f"Unexpected, got {got}") because otherwise, if the format string is the first/only argument, and `got` unexpectedly contain `%`, an exception will be raised due to a missing format parameter. (Also, for the logging functions, you don't pass a tuple, just a normal series of arguments.)
- altncatchfire 6y agoI dont think this is true. I just tried with the following program and it runs as expected (Run on Python 3.7.9). import logging got = "%d" logging.warning(f"hi {got}") logging.debug(f"hi {got}") Output WARNING:root:hi %d DEBUG:root:hi %d
- CGamesPlay 6y agoFrom reading the comments in the discussion here, I'm curious why the f-string syntax is so all-over-the-place? - f"{foo}" uses the default format - f"{foo!r}" uses repr to format - f"{foo=}" uses a special-cased debugging introspection - f"{foo:formatstr}" uses the default format with a custom format string - f"{foo:{bar}}" uses a dynamic format string It seems like `!r` and `=` should have been implemented with `:r` and `:=` instead?
- pansa2 6y agoYeah, there seem to be a number of special cases. The rules for the trailing `=` case are particularly complicated: {x=} -> "x="+repr(x) {x=:.2f} -> "x="+format(x, ".2f") {x=:} -> "x="+format(x, "") {x=:!s:20} -> "x="+format(str(x), "20") {x=:!r:20} -> "x="+format(repr(x), "20") https://bugs.python.org/msg341732 https://bugs.python.org/msg341732
- matmann2001 6y agoWhat if your format string started with "r" or "=", or was simply just one of those characters?
- dragonwriter 6y ago!r is one of a class of conversion flags, others are !a which uses ascii(), !s which uses str(). “=” in the format-spec (the thing that comes after the “:”) is the numeric padding alignment specifier (other alignment specifiers are left “<”, right “>”, and center “^”.)
- CGamesPlay 6y agoOh, neat, so I can stack them: foo = 'foo' print(f"{foo=!r:>20}") print(f"{foo.upper()=!r:>20}") The `=` you pointed out is a special case, however, and not part of the alignment specification. Sadly, this means that the above snippet is not as nicely formatted as I'd like. Still, it does seem useful for these cases: print(f"{x=:.2f} {dx=:.2f} {dot_product=:.4f}")
- hannibalhorn 6y ago
- franklyt 6y agoES6 spec for template literals, while not unique as far as I know, has really been the most intuitive method for templating strings that I’ve seen. It’s a bit implicit so it probably wouldn’t work for Python (in this case, % formatting wins).
- namelosw 6y agoSome experiences of counterparts from languages I've used, although it's like bike shedding but it's still user experience. It would be user-hostile if not carefully designed: Ruby: Good but there are too many kinds of syntax. Use double quote to interpolate which need to escape a lot. And single quote doesn't interpolate, which makes many code bases very mixed and merging code is annoying. I wonder if they swapped them in the first place would it make the mixed thing better because it's easier to settle on single quote? C#: Support multiline, still awkward because it uses double quote. JavaScript: Simple, intuitive yet extensible. It's pretty useful because in JavaScript world embedded languages are very common (CSS, HTML etc). And I find ${} is slightly better than {} in C# and Scala because of the escaping. The community is pretty much settled with single quote, and only use back tick when interpolating so it suck less than Ruby. Scala: Extensible. Very thoughtful and cool, you can indent and strip margin. It's a little bit cumbersome to write multiline when it's space sensitive in JavaScript Elixir: Extremely extensible, the Sigil is not only for strings but also for other syntax. Clojure: It's weird that I put Clojure here, yet I find (str "Hello" name "!") is not far from dedicated interpolation syntax. S-expr, simplicity and all that. I would say Clojure's is good enough for me, but JavaScript is pretty awesome already - maybe it would be perfect if it can strip margin as well.
- drdaeman 6y agoThe issue with f-strings is that they can't be used for i18n. AFAIK, it is impossible to make `_(f"Hello, {name}")` work - at least without a preprocessor or very arcane bytecode manipulation magic. This is unlike much uglier but common and well-supported `_("Hello, %(name)s") % {"name": name}`. In the examples I've used `_` as an alias for a `gettext` call - I believe this is a common practice to do something like `from django.utils.translation import ugettext as _`.
- half-kh-hacker 6y agoNot f-strings, but I am partial to something like: _("Hello, {name}").format(name=name)
- pbsds 6y ago_("Hello, {name}).format(**vars())
- WorldMaker 6y agoA key feature to the recent JS and C# implementations of string interpretation ("template strings") was that both provide a way to pass a template string to a function in a deconstructed form (an array of string parts, and an array of the strings expected to fill their holes). In C# it's a static type expectation (if you cast to a string you get the final formatted string, if you request the FormattableString you get the full deconstructed meta-object) and in JS it's a special function call signature (`` is the template string delimiter and a function that takes the deconstructed parts of a template string is prefixed functionname`example string`). This bit of additional functionality in both JS and C# makes their interpolated strings/template literals extremely useful for i18n (and other uses such as query parameterization and injection attack avoidance). I'm surprised that in comparing the efforts of other languages Python didn't include such functionality in the f-strings PEP. I wonder if such a feature could be added in a non-disruptive way. Maybe a meta-property like a __format_parts__ or something?
- tux 6y agoBefore python had F-Strings, this is one of the reasons why I prefered using Ruby. F-Strings is one of best thing to happen in Python 3.
- mybrid 6y agoWhat if one is printing the output of a function? Then one is left with reverting to positional formats. Mixing positional and named formats is less than robust. One of the common cases for function output is using pprint. As someone who has been using Python since 2.4 print formating using pprint is robust. Over the years I've found it a best practice to only use positional formatting so as to have the format string be stable when switching between usuing a variable or pprint of the variable, especially when debugging. So, I am not using f strings. Another thing is that as a decades long PERL developer I observe Python is becoming the new PERL in having fifty ways to do something. All of these ways will have small-to-large performance differences. Because of this then soon we will all have the Python Cookbook on our shelves to document them so one knows when to part ways with less optimal usages. It is always faster to print an unformatted list then a format string and in fact pylint will ding your code if logger uses a format string and not a list of strings and variables in print order. Then one begins to wonder if this is the case for logger then why not for every print statement? And thus Python is quickly degenerating into PERL code readable territory with each new version.
- mckirk 6y agoWhy wouldn't you be able to use f-strings to print function output?
- angus_gh 6y agoThe reason you get warned for doing `logger.debug("Got %s results", num)` instead of `logger.debug("Got {} results".format(num))` is that in the first case, internally it checks if the logger is disabled and if so skips formatting the string, whereas in the second case you've already formatted it before passing it in, so that work can't be avoided. This isn't the case for print statements because they don't execute conditionally, so you don't ever skip doing the format.
- miguendes 6y agoNice article, I started learning f-strings from it. Shameless plug, last year I wrote one myself to document everything I knew about it. https://miguendes.me/73-examples-to-help-you-master-pythons-f-strings https://miguendes.me/73-examples-to-help-you-master-pythons-...
- oxfordmale 6y agoI do love the Python F-string syntax, however, it shows a weakness of Python as it has been maturing over the years. The original Zen of Python was (PEP 20) There should be one-- and preferably only one --obvious way to do it. There are many ways to format a string, and F-Strings are yet another way. String formatting is not the only area where the original ZEN no longer applies. Of course this is part of a language natural evolution, however, it does make the language more confusing for new starters. Syntax sugar coating Python may also distract from solving more systematic problems, like the lack of proper multithreading in Python.
- akvadrako 6y agoI feel that python has been becoming less and less zen for a long time, starting with the async stuff and most recently with the walrus operator. I used to like it not because it was a great language but because it had a low barrier to entry for casual programmers. Now it's just like a worse Ruby with a lot of momentum from the early years.
- qayxc 6y agoI don't understand. Why exactly is having features that you don't need to use a barrier to entry? Casual programmers can use the same Python that 2.x used - with very little (yet IMO logical) differences. You can get started just as easily and use/learn (or don't) features as you need/wish.
- Majromax 6y agoCasual programmers, especially those learning Python as a first programming language, aren't well-placed to know which language features they need. If they're learning from multiple sources and tutorial series A teaches Python 2-style %-formatting, B teaches str.format, and C teaches f-strings, then the novice programmer will learn all of these equivalent features. This also shows up when the novice programmer wants to understand other people's code. If there is only one way to do things, then all common examples of a task should use that one way. If there are multiple ways (as here), then common examples (or an open source project of interest) may use any combination of them. To understand the code, the novice programmer needs to understand all of the right ways to do something rather than one of them.
- jnwatson 6y agoCould someone put 2016 in the title? f-strings were released in Python 3.6 in December 2016. This isn't news.