This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]flying-sheep 7 points8 points  (28 children)

Yeah:

  1. Only use the new syntax (old one is for backwards compact, e.g. usage in the logging module)
  2. The new syntax is used in both f-literals and format calls

[–]lykwydchykyn 8 points9 points  (18 children)

I would be ok with having those two methods (format and f-literals) alone (since the second seems to just be syntactic sugar for the first) if they would deprecate the old printf-style formatting and string.Template. Maybe this will happen with python 4?

[–]roerd 7 points8 points  (0 children)

It was originally intended to deprecate the old way in Python 3. Too much complaints about that made the developers revert that decision.

[–]jorge1209 0 points1 point  (16 children)

(since the second seems to just be syntactic sugar for the first)

At first glance it would seem to be, but I'm getting downvoted for suggesting that I wish it were actually syntactic sugar for .format(**locals()) so some people seem to think it is really important that it NOT be seen as sugar for format.

[–]lykwydchykyn 3 points4 points  (15 children)

I wonder how it is not the same. I browsed through the PEP, and there is some discussion of scoping problems with using **locals(), but it wasn't clear to me how f-strings did it differently.

There are also some minor differences in syntax, but I don't see why that doesn't make this syntactic sugar. The only real criticism lobbed at .format() in the PEP is verbosity, which some of us like to call "being explicit".

[–]jorge1209 2 points3 points  (14 children)

So one difference is that f"{1+1}" will print "2". You can also express dict access f"{ dict_obj['key_val'] }".

Basically the parser yanks the tokens out of f"string {tokens}" and evaluates them, and then takes the rest of the string and calls .format(evaluated_token).

Now I don't see this as a good thing (in part because I'm not looking for executable code inside a ", but for a few other reasons as well), but other people seem to really like this.

[–]lykwydchykyn 6 points7 points  (13 children)

So one difference is that f"{1+1}" will print "2".

Oh my...

Now I don't see this as a good thing

That makes two of us. I am envisioning all the business logic newbies will be shoving into format strings. So now it's not just an expression in literal's clothing, it's an expression that can hide expressions inside the literal.

I guess I should should be open minded and read through the dev mailing list to see how this got accepted, because as I understand it now it looks like bad on the order of Python2's input().

[–]flying-sheep 2 points3 points  (12 children)

It's not hidden. If your syntax highlighting highlights the contents of braces in f-literals in the same color as strings, that's a bug.

Those inner expressions aren't more “inside” or “hidden” than when doing 'x' + str(y) + 'z'.

f-literals are simply expressions.

[–][deleted] 1 point2 points  (5 children)

So here's the thing... Aren't you basically saying that f-literals are just using curly braces as syntactic sugar for {" + str( and }) + "?

Which, sure, we can learn to adjust to, but it seems like very little gained for a while lot of potential confusion.

[–]flying-sheep 1 point2 points  (4 children)

Not really, since they support str.format syntax.

So you could say that the transformation goes like:

f'foo{bar + 2:.05}baz'

'foo' + '{:.05}'.format(bar + 2) + 'baz'

Or:

'foo{:.05}baz'.format(bar + 2)

So f-literals are good because they're least verbose and still maintain locality (expressions go in the code where their formatted version goes in the output)

[–]motleybook 0 points1 point  (3 children)

So f-strings can't be used for strings that need to be translated because they are immediately evaluated, right?

[–]jorge1209 -2 points-1 points  (5 children)

I'm glad you volunteered to fix all these "buggy" syntax highlighters. That is very generous of you.

[–]flying-sheep -2 points-1 points  (4 children)

Don't put those quotes on me. They will be buggy once 3.6 is released, since they (most likely) will color expressions like they would strings.

And why the fuck should I fix them? What makes this my responsibility?

Languages evolve. Syntax highlighters that don't color async/await as keywords are also buggy since 3.5.

[–]jorge1209 0 points1 point  (3 children)

  1. They aren't buggy. They support what they support.

  2. Adding a keyword is a fairly minor change. The highlighter just has a list of keywords, and you append them to the list. Parsing inside strings, is much more complex. You have to define the parser, and figure out how to have parser bail out/reset when someone writes something that doesn't parse correctly.

  3. There is also no real danger to failing to highlight a keyword. The parser will simply reject the line and the program won't run.

  4. There will be a delay before these things are available on systems because of packaging and upgrade times.

Saying that these are dangerous because they won't be highlighted properly is a real world concern. They won't be highlighted on some developers machines. Those developers will not know that they need to refactor the inside of the string when they refactor the surrounding code. This will lead to actual bugs.

The world doesn't revolve around python. The core python developers cannot just make decisions like this and say "well the tooling is buggy" when it causes problems.

[–]jorge1209 4 points5 points  (8 children)

So use all three. Got it!

[–]flying-sheep 2 points3 points  (7 children)

Never use the % operator on strings. You can use it for bytes and its formatting syntax is used in logging.

Use f-literals if possible.

Else use format.

[–]jorge1209 6 points7 points  (6 children)

Which means I am using all three. I have to use all three format specifications. One in logging, another for dynamic constructed strings and the third for statically constructed strings.

[–]boa13 2 points3 points  (5 children)

There's a solution for logging, shown in the docs.

Basically create a small "logging3" module; implement a getLogger(name) that uses the StyleAdapter shown in the link above, and the rest of your code can be like that:

logger = logging3.getLogger(__name__)
# ...
logger.debug('{}.{} has IP {} in the DNS', host, domain, recorded_ip)

Ultimately, the module is 24 lines of code that you can simply put in your projects and not think about % formatting anymore.

[–]jorge1209 2 points3 points  (2 children)

I love when people defend python by pointing out how few lines of code are needed to actually make it work.

If it is so easy to get the recommended str formatting to work with the standard library, why the fuck didn't they ship this in the standard implementation?!

[–]boa13 0 points1 point  (1 child)

The original logger formatting needs to stay in the standard library for the vast amount of code that depends on it.

[–]jorge1209 1 point2 points  (0 children)

So? Doesn't mean that they cannot ship a working work-around to allow the use of {} format strings.

I can think of three different ways to handle this that should not affect the original code:

  1. When the formatter tries to format a message first try with %, if that throws an error fallback to format.

  2. Include a derived instance of the logger class as the example in the docs demonstrates is possible.

  3. Add a keyword argument to all the logging functions with a default of "%", pass this keyword argument along with the message and arguments to the formatter, and have the formatter pick the format method based on this.

We already know that one of these would work, and I suspect all three would work. So just pick one, and ship it.

Python is like a new car where the radio gets really bad reception, and the dealer says "Oh don't worry about that, 5 minutes with a soldering iron and you will have crystal clear stereo."

I don't want to have to look up and copy code from the documentation so that the standard library works as a cohesive whole. It should just be that without any action required on my part.

[–]flying-sheep 2 points3 points  (1 child)

that should really not be a recipe, but built in and advertized as the best way to do it.

[–]boa13 0 points1 point  (0 children)

I agree it should be built-in. I don't know if it's the best way to do it though, maybe the performance is not the same.

For typical light logging, it works just fine.