News for hackers. Stuff that matters.
Re:Python 3.6.0 released by chrisshroba on Friday December 23, 2016 @10:15AM (#13245073)
I absolutely LOVE format strings. Whereas before, you had to format strings in one of these ways (among other more verbose ways):

  "My name is {} and I am {} years old".format(name, age)

  "My name is %s and I am %s years old" % (name, age)

  "My name is {name} and I am {age} years old".format(name=name, age=age)
Now, we can finally use f-strings, where anything in brackets is eval'ed, with the result subbed in:

  f"My name is {name} and I am {age} years old."
Re:Python 3.6.0 released by perlgeek on Friday December 23, 2016 @10:45AM (#13245254)
As a long-time Perl developer, it's kinda amusing that this is the new big Python feature now, when shells, awk, Perl, PHP and Ruby have had string interpolation for ages.

Yes, I know you can do more with format strings than plain interpolation, but that's all that the basic examples show.

(Also: Perl has allowed underscores in number literals for ages).

Re:Python 3.6.0 released by polotics on Sunday December 25, 2016 @10:05AM (#13254293)
Thanks for the sneer, it really helps the Xmas spirit! I'm almost done fully getting rid of Perl at my corp, it's been an ordeal of unreadable obfuscated mess. I'll be glad when it's fully gone.
Re:Python 3.6.0 released by throwaway7645 on Friday December 23, 2016 @04:39PM (#13247351)
As a python guy learning perl for fun, I've found simple string interpolation and heredocs to be very nice.
Re:Python 3.6.0 released by rantanplan on Friday December 23, 2016 @12:44PM (#13246027)
String interpolation isn't a new feature, you are confused.

Python just got one more way of doing string interpolation.

Re:Python 3.6.0 released by cstrahan on Friday December 23, 2016 @03:32PM (#13247075)
I believe that, when most people say "string interpolation", they mean "first class string interpolation syntax", as opposed to the use of function invocation. I don't have citations, but I hope this perspective helps in some way.
Re:Python 3.6.0 released by TAForObvReasons on Friday December 23, 2016 @12:59PM (#13246156)
printf-style specifiers and substitutions, like in the examples GP provided, aren't considered string interpolation.
Re:Python 3.6.0 released by rantanplan on Friday December 23, 2016 @01:46PM (#13246467)
> "My name is {name} and I am {age} years old".format(name=name, age=age)

This is a textbook example of string interpolation.

You can look it up on wikipedia. I am not sure what you mean.

Re:Python 3.6.0 released by TAForObvReasons on Friday December 23, 2016 @02:17PM (#13246653)
The wikipedia examples are somewhat misleading.

The string "My name is {name} and I am {age} years old" is a plain string. By itself, there is no interpolation:

    >>> x = "My name is {name} and I am {age} years old"
    >>> x
    'My name is {name} and I am {age} years old'
The format function is what confers meaning. This is identical to a standard printf situation, wherein the string "%d %x" encodes a transformation but in and of itself isn't a transformation.

Contrast with the proposal for f-strings:

    >>> x = f"My name is {name} and I am {age} years old"
    # this is an error unless name and age are defined
The literal mandates a particular interpretation, and the f-string cannot exist outside of that in the same way that the standard string does. Or as the PEP puts it:

> It should be noted that an f-string is really an expression evaluated at run time, not a constant value.

Re:Python 3.6.0 released by rantanplan on Friday December 23, 2016 @02:38PM (#13246782)
By itself there's no interpolation, which simply means it's not an expression.

But when you do: "{foo} {bar}" % {"foo": "blah", "bar": "blooh"}, this is string interpolation.

Is there a definition, anywhere(even a book would suffice), where it has your strict definition of what interpolation is?

I'm not trying to undermine the usefulness of the interpolated expressions and what you said. I just really want to know if the definition is so strict.

UPDATE: http://rosettacode.org/wiki/String_interpolation_(included)

I think that you are the one who's making the definition so specific and strict. I can't find a single source on the Internet that agrees with your definition.

Re:Python 3.6.0 released by actsasbuffoon on Saturday December 24, 2016 @09:28AM (#13249876)
Reading the text of the Wikipedia article, I think it does make a distinction between the two styles.

  string interpolation is the process of evaluating a string literal containing
  one or more placeholders, yielding a result in which the placeholders are 
  replaced with their corresponding values.
I read this as saying that the evaluation of the string literal yields a string where the substitution has already been made. That's not the case with the format style of "string interpolation." You provide a template string and call a function with some arguments, and that replaces the placeholders. The string literal does not handle the substitutions itself. I believe the Wikipedia article agrees that this is the deciding factor.

A few sentences later, the article includes this statement:

  Some languages do not offer string interpolation, instead offering a standard
  function where one parameter is the printf format string, and other(s) provide
  the values for each placeholder.
This sounds pretty similar to what was in Python prior to this release (especially if you know how methods work in Python).

Also notice how the statement begins by saying that not all languages offer string interpolation. I think that's the final nail in the coffin of the argument. You could use virtually any language to write a function that handles substitutions in template strings. If that qualified as string interpolation then every language would have it. Since some languages do not, I think we can safely say that we're talking about a syntax feature.

Re:Python 3.6.0 released by douchescript on Friday December 23, 2016 @01:32PM (#13246391)
Dont worry :) soon the other ways will be deprecated. There can be only one _pythonic_ way to do it.
Re:Python 3.6.0 released by soperj on Friday December 23, 2016 @01:54PM (#13246510)
I remember when I started python that one of the big differences was that there wasn't a 1000 ways to do something, but one right way.
Re:Python 3.6.0 released by amyjess on Friday December 23, 2016 @02:47PM (#13246819)
There's always been multiple ways to do something in Python, just one best way, and the best way has been known to change between versions.

See: list comprehensions obsoleting most uses of map() and filter(), then generator expressions obsoleting any uses of list comprehensions that don't need a list object

Here's four ways to do the same thing.

Naïve way:

    foo = []
    for x in bar:
        if meets_some_condition(x):
            foo.append(transform(x))
    do_something_with(foo)
With map/filter:

    do_something_with(map(transform, filter(meets_some_condition, bar)))
With list comprehensions:

    do_something_with([transform(x) for x in bar if meets_some_condition(x)])
With generator expressions:

    do_something_with(transform(x) for x in bar if meets_some_condition(x))
All four of those are valid in modern Python, but the latter and only the latter is the right way to do it.

Before Python 2.0, either map/filter or the naïve way was the right way (TBH, I'm not familiar with what was considered right in the old days). When Python 2.0 came out, list comprehensions became the right way. When Python 2.4 came out, generator expressions became the right way. The right way depends on your version of Python.

Or for that matter, look at old-style classes vs. new-style classes. When new-style classes came out, they immediately became the one and only right way to do classes, but old style classes still stuck around for the rest of the 2.x series.

Re:Python 3.6.0 released by cookiecaper on Friday December 23, 2016 @03:11PM (#13246952)
I'm going to disagree with you there and prefer the "naive way". This is the clear and explicit way to express your intent. Personally, I'm a big fan of a few more lines for more clarity (within reason), and not a fan of unique, language-specific incantations like comprehensions just for the sake of syntactic sugar.

An implicit coding style that requires the reader to look up the semantics of Python's comprehensions (and also makes it harder to unwind/inject debugging/keep line lengths reasonable) is a negative unless you're gaining something besides brevity.

I understand some claim that comprehensions are faster for various reasons, but this is presumably implementation dependent and shouldn't be assumed unless you know it's the case in your implementation. I also understand that the generator expression uses less memory, but the generator version can be written the long way too:

    def test_bar(bar):
        for foo in bar:
            if meets_some_condition(foo):
                yield transform(foo)

    do_something_with(test_bar(bar))
I know that Python is pretty reasonable but I don't like starting the descent down into the alphabet-soup madness that we get in a lot of other languages.
Re:Python 3.6.0 released by ghshephard on Friday December 23, 2016 @04:20PM (#13247280)
But List comprehensions, and now generators, are what python, "Pythonic". I'm a relative novice with python, but they are straightforward to understand, and are fairly consistently used by practitioners of the language.

But, I agree with you regarding creeping complexity in general - @wrappers/decorators confused me for the longest time, and I often wonder how someone not familiar with the language is supposed to understand what they are doing.

Re:Python 3.6.0 released by dragonwriter on Friday December 23, 2016 @10:32PM (#13248422)
> This is the clear and explicit way to express your intent.

I disagree. For the (common) circumstances where genexp's are appropriate, they are also the most clear and explicit way of expressing intent (explicit loops express the mechanism, not the intent.)

For cases where you need a list object, list comprehension are the most direct expression of intent.

There's cases where explicit looping is the most clear expression of intent; usually, this would be where complex logic is needed within each loop iteration.

Re:Python 3.6.0 released by actuallyalys on Friday December 23, 2016 @02:21PM (#13246676)
To be fair, string interpolation is an outlier. Also, Python 3 cleaned up a few areas where there were redundant ways to do something, like standard library modules that had both C and (pure) Python versions.
Re:Python 3.6.0 released by stuaxo on Friday December 23, 2016 @02:09PM (#13246604)
Yup, we could do with a python4 to clean things up ...
Re:Python 3.6.0 released by tannhaeuser on Friday December 23, 2016 @12:19PM (#13245872)
awk doesn't have string interpolation, though.

--- "Avoid programming languages starting with the letter P except Prolog"

Re:Python 3.6.0 released by throwaway7645 on Friday December 23, 2016 @04:38PM (#13247343)
Do you use Prolog in day-to-day work?
Re:Python 3.6.0 released by tannhaeuser on Friday December 23, 2016 @04:56PM (#13247419)
Currently, no. But I've implemented an (ISO-)Prolog engine (twice, even). Tragedy is, there are more Prolog engines than Prolog programs, just as there are more web frameworks than web apps ¯\_(ツ)_/¯
Re:Python 3.6.0 released by wyldfire on Friday December 23, 2016 @10:38AM (#13245215)
I would frequently use

   "My name is {name} and I am {age} years old".format(**locals())
... but I always felt a little guilty like this is some kind of dynamic-programming-on-steroids trick that I should use sparingly.
Re:Python 3.6.0 released by cauthon on Friday December 23, 2016 @12:00PM (#13245756)
Ha, I love that trick but always feel guilty because it's breaking "explicit is better than implicit" and someone on the internet told me it's poor style. Very happy the new style was implemented.
Re:Python 3.6.0 released by otakucode on Friday December 23, 2016 @12:14PM (#13245842)
Your guilt was right.
Re:Python 3.6.0 released by amyjess on Friday December 23, 2016 @10:34AM (#13245185)
Yeah, this is the biggest reason why I'm excited about 3.6. I'm still using % for all formatting because .format() is so ugly and verbose, but I'll switch to f-strings when I actually get the opportunity to use 3.6. In fact, when the PEP first appeared I immediately decided that from now on, I'd write all greenfield projects in Python 3 just so I can take advantage of f-strings as soon as I can use 3.6 (yes, this of all things was what made me switch from 2 to 3).

Though I think it would be cool if you could have a deferred format string, like so:

    template = 'My name is {name} and I am {age} years old. Next year I will be {age + 1}!'
    name = 'Amy'
    age = 32
    formatted = template.apply()
    # formatted = 'My name is Amy and I am 32 years old. Next year I will be 33!'
So that way you could store the template as a constant somewhere and apply it after receiving input from the source of your choice.

You can kind of do that with [0], but it doesn't evaluate expressions (so the age + 1 thing won't work).

    [0] .format(**locals()) # putting it here because HN eats the asterisks if it's not indented
Re:Python 3.6.0 released by spraak on Friday December 23, 2016 @10:58AM (#13245332)
What about..

    def template(name, age):
        return f"My name is {name} and I am {age} .."
    formatted = template('Amy', 32)
?

[Edit: I only started learning Python recently, and this is how I'd think of doing it come from JS:

    const template = (name, age) => `My name is ${name} and I am ${age}`; 
    const formatted = template('Amy', 32);

]
Re:Python 3.6.0 released by mixmastamyk on Friday December 23, 2016 @03:16PM (#13246989)
Check string.Template from the docs.
Re:Python 3.6.0 released by bastawhiz on Friday December 23, 2016 @10:40AM (#13245222)
You can always put your f-string in a lambda if you don't want to evaluate it immediately. Pulling values from the scope of the function you call your apply() method from, though, is some scary magic. I wouldn't be confident that I hadn't screwed that up.
Re:Python 3.6.0 released by bluecalm on Friday December 23, 2016 @02:50PM (#13246832)
Yeah, using templates like that is something you can do with format (and then just do template.format(arg_name1 = something1, arg_name2 = something2) and it seems like usecase important enough to not use f-string exclusively.
Re:Python 3.6.0 released by mixmastamyk on Friday December 23, 2016 @02:34PM (#13246766)
Cool. string.Template might be what you are looking for.
Re:Python 3.6.0 released by kbutler on Friday December 23, 2016 @10:24AM (#13245124)
But I really miss "There should be one-- and preferably only one --obvious way to do it."

(Conflict between improvement and backward compatibility, but I miss it.)

Re:Python 3.6.0 released by bbatha on Friday December 23, 2016 @10:31AM (#13245165)
The quote in context shows no conflict:

    In the face of ambiguity, refuse the temptation to guess.
    There should be one-- and preferably only one --obvious way to do it.
    Although that way may not be obvious at first unless you're Dutch.
It pretty clear that if you support python 3.6 you should use `f` strings.
Re:Python 3.6.0 released by kbutler on Friday December 23, 2016 @01:31PM (#13246387)
> It pretty clear that if you support python 3.6 you should use `f` strings.

And if you support any earlier version of python, you cannot use f strings. Hence, there is a conflict between improvement and backward compatibility.

Re:Python 3.6.0 released by ghshephard on Friday December 23, 2016 @04:24PM (#13247294)
But for new projects, where you can chose the version of Python you want, there is no requirement for "backwards compatibility" - it's not as though Python 3.6 isn't available for every platform that 3.5 was on.
Re:Python 3.6.0 released by mixmastamyk on Friday December 23, 2016 @02:52PM (#13246839)
Yeah! Believe it or not (shameless plug) it was yours-truly who finally got the idea to snowball through the gauntlet at python-ideas. The extent of my participation, but still, am happy with how it all turned out. (pat pat)

Python is no longer inferior to Bash and Perl for short scripts and more concise. Looking across the modern language landscape, this functionality is becoming an industry standard. The format string is also a little more sophisticated that it looks on the surface.

Still, I was a bit down that so many showed up to complain about it, mostly with purity or adversity-builds-character arguments. I'm glad lots of people are enjoying it.

Re:Python 3.6.0 released by aeijdenberg on Friday December 23, 2016 @03:39PM (#13247118)
The problem I've found is that when we start using cute tricks like the old "xxx" % locals(), or the newer f-strings, is that we tend to start forgetting basics like escaping the strings that we're substituting in.

For the example above, this might be fine, but most examples that I see using this kind of thing are things like:

"select * from foo where id = '%s'" or "<input type='hidden' name='foo' value='%s'/>" or "rm -rf /path/to/customer/%s"

I tend to think that in general appending strings is more often than not quite a dangerous operation, and it behooves the caller to need to have a long think about exactly how the strings being substituted in need to be escaped - as it's pretty rare that they don't need to be.

Re:Python 3.6.0 released by golergka on Friday December 23, 2016 @11:13AM (#13245434)
So python now has 4 different ways to do this? Ugh.
Re:Python 3.6.0 released by Sean1708 on Friday December 23, 2016 @12:09PM (#13245822)
It's a fair complaint but they tried deprecating % and that ... well ... ended badly.
Re:Python 3.6.0 released by cookiecaper on Friday December 23, 2016 @01:49PM (#13246485)
Yep, can't please everyone. Someone is going to complain that you broke backwards compatibility if you remove something, and someone is going to complain that you've muddied up the language if you don't. Python 3 was an attempt to straddle that chasm and it definitely caused problems.

I think you just have to play it by ear. Old formatting styles seem like one of those things you want to leave alone for a really long time, because they're so common that it'd be a major PITA to fix all occurrences, and because it doesn't seem like there's an obvious negative ramification to allowing the old styles to continue to evaluate according to the old methods.

Re:Python 3.6.0 released by mixmastamyk on Friday December 23, 2016 @02:31PM (#13246746)
It is built right on top of .format(), so perhaps it's more accurate to say 3.1 ways.
Re:Python 3.6.0 released by rwmj on Friday December 23, 2016 @01:53PM (#13246503)
My colleague found a little bug in his code when he upgraded to Python 3.6 last week. Python ≤ 3.5 allowed "\o" (evaluating it as a backslash followed by a letter o). Python 3.6 now (correctly) treats this as a warning.
Re:Python 3.6.0 released by makecheck on Friday December 23, 2016 @12:09PM (#13245818)
I’m curious actually why an f-string syntax is considered OK when a print-statement is not.

I mean, there’s a precedent for short built-in functions like len(), and apparently 'print "x"' as a statement was terrible so 'print("x")' replaced it. If they wanted such consistency, shouldn’t they have used 'f("{a} {b}")' or something? Or alternately, allow p-strings like 'p"hello, world"' to print?

Re:Python 3.6.0 released by richard_todd on Friday December 23, 2016 @12:14PM (#13245841)
That would not be a traditional function, because you want {a} to be evaluated in the context of the caller, rather than the context of f.
Re:Python 3.6.0 released by sirtaj on Friday December 23, 2016 @12:40PM (#13245996)
As I understand it, print() was created not from a sense of consistency, but because a few awkward cases could be simply solved by turning print from a statement into an expression. Use in lambdas would be one example.
Re:Python 3.6.0 released by mixmastamyk on Friday December 23, 2016 @03:00PM (#13246893)
I don't like the extra parens, but I made an editor shortcut to alleviate that.

Other cases such as redirecting to a file, and changing the line ending are handled much more intuitively with the print function.

Re:Python 3.6.0 released by rtpg on Friday December 23, 2016 @02:49PM (#13246828)
In order to implement `f` as a plain function with same behaviour as g-strings, you would probably have to do some stack trickery to properly capture locals.

In fact, I don't think it would be possible to write such a function in the general case, because it's ambiguous what environment you would need to use.

For example:

def call(func, arg):

    a = 2

    return func(arg)
a = 1

print(call(f, "{a}"))

Does this print 1 or 2?

Re:Python 3.6.0 released by dragonwriter on Friday December 23, 2016 @10:38PM (#13248435)
> I’m curious actually why an f-string syntax is considered OK when a print-statement is not.

One reason might be that f-strings work in expressions, while statements do not. Even with the BDFLs apparent distaste for functional programming constructs, syntax that works within expressions has lots of practical advantages over statements.

Re:Python 3.6.0 released by NegativeLatency on Friday December 23, 2016 @01:06PM (#13246198)
The prefix isn't new, there were already u, b, and r strings.
Re:Python 3.6.0 released by sakubara on Friday December 23, 2016 @10:22AM (#13245114)
I can imagine this to be especially useful in implementing __repr__ and __str__ for classes.
Re:Python 3.6.0 released by coredog64 on Friday December 23, 2016 @01:10PM (#13246225)
When can I expect to import this into 2.7.x from future?
Re:Python 3.6.0 released by has2k1 on Friday December 23, 2016 @01:12PM (#13246235)
It is my understanding that the __future__ module is frozen.
Re:Python 3.6.0 released by xrange on Friday December 23, 2016 @02:26PM (#13246712)
You might want to check out Python 2.8

https://www.naftaliharris.com/blog/why-making-python-2.8/

Re:Python 3.6.0 released by spraak on Friday December 23, 2016 @10:55AM (#13245306)
Does anyone else find Python just as 'crazy' as Javascript gets called?
Re:Python 3.6.0 released by Sean1708 on Friday December 23, 2016 @12:05PM (#13245799)

  >>> 1 + '1'
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  TypeError: unsupported operand type(s) for +: 'int' and 'str'
No.
Re:Python 3.6.0 released by crazedpsyc on Friday December 23, 2016 @12:16PM (#13245851)

    >>> float('inf') < 'hi'
    True
Fixed in Python 3, but still. There are more of these things.
Re:Python 3.6.0 released by teaearlgraycold on Friday December 23, 2016 @01:05PM (#13246196)
So, you mean it was fixed 8 years ago, then.
Re:Python 3.6.0 released by bradbeattie on Friday December 23, 2016 @01:28PM (#13246362)
Re:Python 3.6.0 released by lmm on Friday December 23, 2016 @11:34AM (#13245586)
No. Python is probably the least crazy mainstream language IMO. (Except the middle-endian for comprehensions, and the silly truthiness definition for dates)
Re:Python 3.6.0 released by spraak on Friday December 23, 2016 @11:47AM (#13245666)
I find it weird that there are built-in functions like `map` but string objects still have methods like `split` - but I am so used to everything being a method on an object in JS.

I mean that it feels so much nicer to do:

    let x = [ 1, 2, 3].map(i => i * 10)
than

    x = list(map((lambda i: i * 10), [ 1, 2, 3 ]))
Re:Python 3.6.0 released by lyschoening on Friday December 23, 2016 @12:19PM (#13245870)
The pythonic (i.e. right) way to write this is:

    x = [i * 10 for i in [1, 2, 3]]
Re:Python 3.6.0 released by SeanDav on Friday December 23, 2016 @12:57PM (#13246133)

    i. let x = [ 1, 2, 3].map(i => i * 10)

    ii. x = list(map((lambda i: i * 10), [ 1, 2, 3 ]))

    iii. x = [i * 10 for i in [1, 2, 3]]
All these methods are really just multiple sides of the same coin. All I see is different syntax, none looks any better from my point of view and unless there is a clear performance benefit, just use what you are used to, or what your coding standards define.
Re:Python 3.6.0 released by lyschoening on Friday December 23, 2016 @01:22PM (#13246320)
ii. vs iii. boils down to explicit rather than implicit. iii. is simply easier to understand. I believe most people would also agree that ii. doesn't look particularly nice.
Re:Python 3.6.0 released by NegativeLatency on Friday December 23, 2016 @01:08PM (#13246217)
I think in general the comprehension is faster because of how method calls and symbol lookups work in python. For the most part it's just a style thing.
Re:Python 3.6.0 released by spraak on Friday December 23, 2016 @10:08PM (#13248366)
I understand superficially that this works, but can you explain the syntax (or is there a name for this)? So far I've also seen a for loop like

    for i in [1,2,3]:
        ...
Re:Python 3.6.0 released by spraak on Friday December 23, 2016 @11:46PM (#13248620)
Re:Python 3.6.0 released by lmm on Friday December 23, 2016 @11:56AM (#13245722)
There's a lot of value in having the option of using top-level functions e.g. len. Sometimes something is more natural as a method, sometimes it's more natural as a function. In the specific case I probably agree with you, but I don't think any of the differences qualify as "crazy"; generators being lazy by default is a reasonable choice (though I think the wong one), map is often used partially applied so it's nice to have it available as a function, => was only recently added to javascript and I would hope Python will get something similar sooner or later (though I'd rather have scala-style _).
Re:Python 3.6.0 released by ufmace on Friday December 23, 2016 @02:46PM (#13246812)
I read that apparently it is impossible to add a true lambda functionality to Python because of the whitespace blocks thing. There's intentionally no way to block multiple statements together except to have them on multiple lines sharing an indent level, which means there's no way to write a lambda longer than a single expression and have a syntax for it that makes sense.
Re:Python 3.6.0 released by lmm on Friday December 23, 2016 @07:14PM (#13247961)
That issue has never really bothered me tbh. If you're writing in functional style then everything tends to be a single expression anyway, and in the rare cases where you need statements the function tends to be complex enough to be worth naming IMO.
Re:Python 3.6.0 released by otakucode on Friday December 23, 2016 @12:20PM (#13245881)
What does the _ do in Scala? I've not used Scala, and it wasn't terribly clear from context unless you mean they use _ exactly as javascript uses => (which would seem really weird to me so I'm assuming its different, apologies if that is asinine).
Re:Python 3.6.0 released by Nullabillity on Friday December 23, 2016 @02:27PM (#13246720)
_ is a placeholder for the next lambda argument. So, for example, `x => x.blah()` could be rewritten as `_.blah()`, and `x => blah(x)` could be rewritten as `blah(_)` (or, since we don't need partial application here, simply `blah`).

It also works for multiple arguments: `_ * _` is equivalent to `(x, y) => x * y`, which is really `(x, y) => x.*(y)`.

Re:Python 3.6.0 released by lmm on Friday December 23, 2016 @12:42PM (#13246015)
"_" is kind of the equivalent of "x => x" (only rather than x it's some temporary generated name that doesn't overlap with anything else). So you'd write "_ * 10" rather than "i => i * 10"
Re:Python 3.6.0 released by spraak on Friday December 23, 2016 @12:03PM (#13245784)
All good points, thanks for explaining!
Re:Python 3.6.0 released by emidln on Friday December 23, 2016 @11:05AM (#13245373)
Obligatory xkcd: https://xkcd.com/927/

In typical perl^Wpython spirit, we now can save a few characters by typing:

    f"My name is {name} and I am {age} years old."
instead of:

    "My name is {name} and I am {age} years old.".format(**locals())
Look how much typing we saved! Now we just have to explain to new users that their strings get eval'd for stuff in brackets, but only when the string is prefixed by `f`...
Re:Python 3.6.0 released by munchhausen on Friday December 23, 2016 @11:09AM (#13245403)
> "My name is {name} and I am {age} years old.".format(locals())

Well that's a definite code smell.

Re:Python 3.6.0 released by emidln on Friday December 23, 2016 @11:58AM (#13245745)
Don't realize that this is exactly what f-strings are doing? If you accept the `f""` version but not the `"".format_map(locals())`, you're lying to yourself[1]. At least the `str.format_map(locals())` isn't magic (and IMO, much more pythonic (by virtue of explicit), although not as pythonic as `str.format(template_var=scoped_var)`).

The only way `str.format_map(locals())` is a code-smell is if the people working on the code don't actually know Python. Builtin methods of core types, dictionary unpacking, and builtin functions are all extremely common and only complete novices at python would find any issue with them. Compare that to what you need to know when you encounter `f"hello {name}"`:

0) am I working with Python 3.6+?

1) when does replacement happen?

2) what scopes are examined for substitution?

Of course, 2) is a trick question because you EVALUATE ARBITRARY PYTHON EXPRESSIONS IN THE FORMAT STRING![2]

[1] Technically, PEP-0498 states that we're not exposing a full locals() or globals(), but this actually matters very little. The danger here is that both .format() and f"" use object.__format__() (falling back __repr__ IIRC) to figure out the replacement. If your object.__format__() is malicious, it gets access to more data than it needs. However, since python has zero security model, if it's malicious, it pings whatever command and control server it's reporting to and starts up a thread listening for commands anyway (aka you're fucked). Realistically, this means that, you might leak credentials (or other sensitive information) to third party loggers, but only if your method for censoring outgoing log/exception data is a a poorly-built blacklist.

[2] https://www.python.org/dev/peps/pep-0498/#supporting-full-py...

Re:Python 3.6.0 released by cookiecaper on Friday December 23, 2016 @01:55PM (#13246519)
Yeah, I don't really have a problem with the old way:

    print("My robot's name is {}. It's {} years old.".format(name, years_old))
Seems reasonable to me. I don't really see any reason to hack in the locals splat. I guess the one thing it saves you is ordering issues, but in practice, I haven't found those to be terribly inconvenient, and in the rare case they are, you can just do "{name}, {age}".format(name=name, age=years_old)).

Yes, both forms are a little longer, but as you state, it's easier to see what's going on. One of Python's biggest attractions, IMO, is that it's batteries included, but not batteries-sealed-under-1000-layers-of-carbonite-locked-by-a-mystical-incantation, like many other dynamic languages. It's usually pretty easy to get in there and see what's happening without having to traverse tons of indirection due to Python's design principle favoring explicit programming styles.

I won't lie and say I've never wished for more conventional style string interpolation, but it certainly wasn't a showstopper, and it's not something we should assume we get for free.

Re:Python 3.6.0 released by emidln on Friday December 23, 2016 @02:17PM (#13246648)
I was just using the locals() to show equivalence. I think I've done that maybe a handful of times in production code, and it was always an already hairy templating situation. Most of the time, I see:

    "{} world!".format('hello')
or

    "{greeting} world!".format(greeting='hello')
There is a huge value in Python's historical approach of straightforward code built on a relatively small foundation. f-strings are another example of the Python I fell in love with growing up/moving on.
Re:Python 3.6.0 released by mixmastamyk on Friday December 23, 2016 @02:37PM (#13246778)
Sorry, you're wrong. First, it's asterisk asterisk locals(), they don't show here for some reason.

Second, the parser at compile time pulls out the actual expressions and fills them in to a .format call. It _does not_ use locals!

... and your worries about security are not useful, you could make the same statements about an entire code base.

Re:Python 3.6.0 released by emidln on Friday December 23, 2016 @03:36PM (#13247105)
> Sorry, you're wrong. First, it's asterisk asterisk locals(),

I meant what I wrote. Someone, in a rush to criticize about a topic on which they are ignorant, didn't consult the Python documentation:

https://docs.python.org/3/library/stdtypes.html#str.format_m...

> they don't show here for some reason.

Asterisks only show in code blocks. They denote italics in HN's markup. Double asterisks outside of a code block begin and end empty italicized text.

    ** no italics because we're in a code block
> Second, the parser at compile time pulls out the actual expressions and fills them in to a .format call. It __does not__ use locals!

I'll quote myself here, since you missed the first part of this sentence when jumping to a conclusion about why security is irrelevant, but we need to blacklist a well-understood and widely used builtin function anyway.

>> Technically, PEP-0498 states that we're not exposing a full locals() or globals(), but this actually matters very little.

But, while we're trying to be pedantic, let's actually be pedantic:

    >>> import dis
    >>> dis.dis('f"{foo}"')
    
        1           0 LOAD_NAME                0 (foo)
                    2 FORMAT_VALUE             0
                    4 RETURN_VALUE
Shouldn't I be seeing some LOAD_CONST, LOAD_ATTR, and CALL_FUNCTION somewhere?

    >>> import dis
    >>> dis.dis('"{foo}".format("bar")')
    
      1           0 LOAD_CONST               0 ('{foo}')
                  2 LOAD_ATTR                0 (format)
                  4 LOAD_CONST               1 ('bar')
                  6 CALL_FUNCTION            1
                  8 RETURN_VALUE
If you'd like, you can go ahead and examine what it is that FORMAT_VALUE does (hint: invokes PyObject_Format which is defined in abstract.c around line 670 in the Python-3.6.0 tarball).
Re:Python 3.6.0 released by mixmastamyk on Friday December 23, 2016 @04:01PM (#13247213)
The guy that implemented it (Eric) said that it is largely syntactic-sugar where the byte compiler splits the string and converts it into a format(string, args) call. Therefore it gets exactly/only what it needs.

It is true that I've never inspected the C code implementation myself, but I took him at his word.

load_name looks like it read foo, then format_value did format('{}', foo).

I didn't realize it before, but it looks like it might have better performance.

Re:Python 3.6.0 released by emidln on Friday December 23, 2016 @04:27PM (#13247302)
That was the original implementation, AFAICT from reading the ticketing. FORMAT_VALUE was introduced to fix a slight inconsistency where someone might monkey with str.format(). While doing so, it was mentioned that there was a slight performance improvement to fewer lookups. See here for more info: https://bugs.python.org/issue25483
Re:Python 3.6.0 released by mixmastamyk on Friday December 23, 2016 @04:38PM (#13247344)
That's ringing a bell now, the implementation has some elegance to it.