Funny (as in debugging-useful) print format in Python - Page 1
Forum section: electronics / general

author: RoGeorge
author_rank: Super Contributor
timestamp: February 14, 2025, 01:58:29 am
content: Found in these random YT suggested videos fromIndentlychannel:https://youtu.be/EoNOWVYKyo0https://youtu.be/aa39jL7wdJsCode:a: int = 5000b: int = 10000print(f'{a + b = }')in python 3 will print "a + b = 15000"Or,print(f'{a + b = :_.2f}')will print "a + b = 15_000.00"

author: bpiphany
author_rank: Regular Contributor
timestamp: February 14, 2025, 06:59:55 am
content: Python 3.11 or 12 or so to be more specific. Where you may not yet be depending on how far back on the LTS chain you exist.

author: SiliconWizard
author_rank: Super Contributor
timestamp: February 14, 2025, 09:28:44 am
content: Yeah, f-strings allow some funky stuff.https://realpython.com/python-f-strings/

author: janoc
author_rank: Super Contributor
timestamp: February 14, 2025, 10:30:46 am
content: F-strings are old news, this was added in Python 3.6, back in 2016. I.e. almost a decade back:https://www.python.org/downloads/release/python-360/PEP 498, Literal String Formatting

author: tszaboo
author_rank: Super Contributor
timestamp: February 14, 2025, 10:50:45 am
content: Quote from: RoGeorge on February 14, 2025, 01:58:29 amIMHO this falls under the category of programming for side effects.Which is bad practice.f string is there to substitute a part of a string with something. So f'{a]' is OK. f'{max(a)}' is questionable, your example is bad practice. Only reason to do it is if there is like a list comprehension or some other code quality reasons, and still you want to have a lot of comments on what the line does. Because 5 minutes from now nobody is going to understand it.

author: DiTBho
author_rank: Super Contributor
timestamp: February 14, 2025, 11:04:53 am
content: Quote from: tszaboo on February 14, 2025, 10:50:45 amAn almost incredible tale of "triumph" and "tragedy", but ...... there is actually people who writes book about this stuff.what?I saw books in Japan about this for CASIO graphing programming calculators.Practices used onto CASIO Basic + ucPython to take the control via exploitsmade on "side effects" that break the interpreter.

author: golden_labels
author_rank: Super Contributor
timestamp: February 14, 2025, 03:00:57 pm
content: Quote from: tszaboo on February 14, 2025, 10:50:45 amThis expression has no side effects. That=is a part ofthe formatting language. This way you can print the expression and its value without having to duplicate code. Seethe last example. RoGeorge just used a more complicated expression.

author: ejeffrey
author_rank: Super Contributor
timestamp: February 15, 2025, 04:11:24 am
content: Quote from: tszaboo on February 14, 2025, 10:50:45 amIt's neither programming for side effects not bad practice.QuoteWhich is what it does?QuoteIt displays the value of the expression? I honestly don't know what your are getting at.

author: golden_labels
author_rank: Super Contributor
timestamp: February 15, 2025, 06:45:36 am
content: I suspect tszaboo took that for some obscure quirk of Python. His reply would make sense then.

author: tszaboo
author_rank: Super Contributor
timestamp: February 15, 2025, 09:55:52 am
content: Quote from: ejeffrey on February 15, 2025, 04:11:24 amIt's bullshit side effect of a design feature, that wasn't intended to be used this way.Programmers who write code like this make code like this make unmaintainable code.It's the equivalent of bragging: "Look at me. I'm so smart. I made a calculation implicit instead of explicit. I also obfuscated the code and made it hard to read and understand. "Nobody likes these people.

author: jfiresto
author_rank: Frequent Contributor
timestamp: February 15, 2025, 10:21:33 am
content: Quote from: tszaboo on February 15, 2025, 09:55:52 amExcept that is isn't. The language grammar page gives the syntax.The proposalfor its implementation explains its rationale.

author: tszaboo
author_rank: Super Contributor
timestamp: February 15, 2025, 10:28:13 am
content: It is bullshit.It might print "a + b = 15000" or "5000 + 10000 = " or SyntaxError: invalid syntaxYou have no idea what it should print. Bad practice.There is a more understandable and explicit way of achieving the same.

author: ejeffrey
author_rank: Super Contributor
timestamp: February 16, 2025, 03:45:57 pm
content: Quote from: tszaboo on February 15, 2025, 10:28:13 amI definitely know exactly what it should print. It's an explicit design feature and everybody who uses it knows what it does.It's also the best way to implement it because there is no chance of mismatch between the printed text and the evaluated expression. For instance if you do print(f"a+b={a + b}") and later modify it to print(f"a+b={a+b+c}") you are going to have incorrect diagnostic messages. Same thing if you use a separate temporary variable in the same way. print(f"{a+b=}") on the other hand works correctly with less opportunity for error.

author: bpiphany
author_rank: Regular Contributor
timestamp: February 17, 2025, 11:58:13 am
content: I was wrong. The equals thing for f-strings was added in 3.8. I believe it was primarily meant as a convenience for debugging, not production code so much.

author: ejeffrey
author_rank: Super Contributor
timestamp: February 17, 2025, 06:41:05 pm
content: Quote from: bpiphany on February 17, 2025, 11:58:13 amI would say it's useful for both debugging (a development task) and logging (a production task). I wouldn't use it for a something end user visible or for persistent serialization. But that has nothing to do with "side effects" or it being poorly defined. It's just usually not desirable to export your internal identifier names to end users or to make it part of your behavior contract.

author: John B
author_rank: Frequent Contributor
timestamp: February 17, 2025, 08:25:40 pm
content: I don't really mind that it does it, however it does seem to lack logical consistency in that the curly brackets should be evaluated like a normal line of code. It looks like it should throw a syntax error.But then again, I'm not a python pythonista who thinks pythonically.

author: ejeffrey
author_rank: Super Contributor
timestamp: February 18, 2025, 04:42:41 am
content: Quote from: John B on February 17, 2025, 08:25:40 pmIt is slightly inconsistent but the fstring already allowed formatting specifiers in the same location like f"{x:.5f}". So it's never been the case that the entire thing in braces is evaluated as an expression. The way it is inconsistent is that it doesn't map to a .format() option because this behavior doesn't make sense for the format method.

author: golden_labels
author_rank: Super Contributor
timestamp: February 18, 2025, 07:40:51 am
content: Permitting format specifiers is no more inconsistent than allowing expressions.F-strings are Python’s implementation of string interpolation. To be exactly consistent with the pure concept of string interpolation, an f-string should permit nothing more than a single variable name inside curly braces. The style PHP and sh do this with$(without{…}).Most languages do extend this basic concept for programmer’s convenience. So does Python, giving us both: expressions and format specifiers. The only quirk of Python is, that it accepts unexpectedly wide range of expressions.(1)But not only it hasn’t been touched in this topic, but what it makes “inconsistent with the idea” is expressions themselves, not the format specifiers.Note that it should really be “intuitional consistency,” not “logical consistency.” If we’re to be precise.(1)Those are valid f-strings, despite you probably don’t want them to be:Code:a = f'{" "}'print(a)x = 0b = f'{(x:=4)}'print(b, x)y = 10c = f'{#y}'print(c)o = {'Foo': 'Bar'}d = f'{o.pop("Foo")}'print(d)it = iter([0, 1, 2, 3])e = f'{next(it)} {next(it)} {next(it)} {next(it)}'print(e)z = Noneg = f'{(z := lambda x: x + 4, z(10))}'print(g)

author: SiliconWizard
author_rank: Super Contributor
timestamp: February 18, 2025, 10:12:44 am
content: The problem with allowing arbitrary expressions in string formats is to allow unwanted execution of code, obviously. How does Python protect against that?

author: bpiphany
author_rank: Regular Contributor
timestamp: February 18, 2025, 11:04:26 am
content: But it's not arbitrary any more than what you as the programmer put in there, is it? It's perfectly possible to "execute" arguments to f-formatting in other languages too. The only thing the '=' does is to output the argument as a string as well as the result of it, if I understand it correctly.

author: ejeffrey
author_rank: Super Contributor
timestamp: February 18, 2025, 05:40:57 pm
content: Quote from: SiliconWizard on February 18, 2025, 10:12:44 amf-string parsing is part of the parser so it only works on string literals, never user input strings. Essentially the whole f-string implementation is syntactic sugar around the "format" method.Code:print(f"{x}, {y}, {z}")is transformed by by the parser into:Code:print("{0}, {1}, {2}".format(x, y, z))Outside of eval() there isn't even an API that would let you inject arbitrary expressions into the formatting code. It's still true that it puts executable code into something that might look like a string literal. However any code editor will syntax highlight it as code not string literals.The = operators is also handled specially by the parser.Code:print(f"{x=}, {y+z}")is transformed into:Code:print("x={0}, y+z={1}".format(x, y+z))That is, the parser takes the expression between braces and inserts it both as text into the resulting string literal and also as an argument to the format method.

author: SiliconWizard
author_rank: Super Contributor
timestamp: February 19, 2025, 12:17:57 am
content: Quote from: ejeffrey on February 18, 2025, 05:40:57 pmOk. If there's absolutely no means of building a f-string dynamically, then it kind of answers my question.There's a simple way of circumventing this though, and it's by using eval():Code:x, y, z = 1,2,-3F1 = "{x+y*z}"eval('f"' + F1 + '"')Now you may reply that it's a potential security issue that has more to do with eval() than with f-strings themselves. But can you build and use f-strings dynamically (resorting to eval()), yes.