
What's Coming in Python 3.8 - superwayne
https://lwn.net/SubscriberLink/793818/0c6f9dd271021cd4/
======
kbd
Despite controversy, walrus operator is going to be like f-strings. Before:
"Why do we need another way to..." After: "Hey this is great".

People are wtf-ing a bit about the positional-only parameters, but I view that
as just a consistency change. It's a way to write in pure Python something
that was previously only possible to say using the C api.

~~~
stefco_
f-strings are the first truly-pretty way to do string formatting in python,
and the best thing is that they avoid all of the shortcomings of other
interpolation syntaxes I've worked with. It's one of those magical features
that just lets you do _exactly_ what you want without putting any thought at
all into it.

Digression on the old way's shortcomings: Probably the most annoying thing
about the old "format" syntax was for writing error messages with parameters
dynamically formatted in. I've written ugly string literals for verbose,
helpful error messages with the old syntax, and it was truly awful. The long
length of calls to "format" is what screws up your indentation, which then
screws up the literals (or forces you to spread them over 3x as many lines as
you would otherwise). It was so bad that the format operator was more
readable. If `str.dedent` was a thing it would be less annoying thanks to
multi-line strings, but even that is just a kludge. A big part of the issue is
whitespace/string concatenation, which, I know, can be fixed with an
autoformatter [0]. Autoformatters are great for munging literals (and diff
reduction/style enforcement), sure, but if you have to mung literals tens of
times in a reasonably-written module, there's something very wrong with the
feature that's forcing that behavior. So, again: f-strings have saved me a ton
of tedium.

[0] [https://github.com/python/black](https://github.com/python/black)

~~~
bulatb
_> If `str.dedent` was a thing_

Have you looked at textwrap.dedent?

~~~
stefco_
Yes! `textwrap.dedent` is great. On further reflection `wrap` is actually more
useful for this kludge (see below). But my point is that that's a whole import
for a kludge. Compare the f-string ideal (by my standards):

    
    
      raise ValueError("File exists, not uploading: "
                       f"{filename} -> {bucket}, {key}")
    

...which is short enough that it's readable, and it's clear where exactly each
variable is going. It's the single obvious solution, so much so that I don't
spend a second thinking about it (very Pythonic!). Compare it to using
`str.format` with the same continued indentation:

    
    
      raise ValueError(("File exists, not uploading: {filename} -> "
                        "{bucket}, {key}").format(filename=filename,
                                                  bucket=bucket,
                                                  key=key))
    

Even this minimal example looks terrible! Remember that a lot of exceptions
are raised within multiply-nested blocks, and then format pushes things
farther to the right (while also ruining your automated string-literal
concatenation, hence the extra parentheses), leaving very little room for the
format arguments. You can use a more self-consistent and readable indentation
strategy:

    
    
      raise ValueError(
          (
              "File exists, not uploading: {filename} -> "
              "{bucket}, {key}"
          ).format(filename, bucket, key)
      )
    

This is unquestionably more pleasant to read than the former, but it's _3
times longer_ than the simple f-string solution, and I would argue it is not
any more readable than the f-string for this simple example. My point with
having a `str.wrap` builtin is that at _least_ you could use the docstring
convention of terminating multi-line strings on a newline, which would get rid
of the string concatenation issues while leaving you a consistent (albeit
diminished by the "wrap" call) amount of rightward room for the `format` args:

    
    
      raise ValueError("""File exists, not uploading: {filename} ->
                       {bucket}, {key}
                       """.dedent().format(filename=filename,
                                           bucket=bucket, key=key))
    

Maybe a little bit better than the first one, especially if you're writing a
longer docstring and don't want to think about string concatenation. But still
a kludge. You can use positional formatting to shorten things up, but the
fundamental weakness of `str.format` remains.

~~~
bpchaps
Here's a clean way to do that:

    
    
      str_fmt = "File exists, not uploading: {filename} -> {bucket}, {key}"
      fmt_vals = dict(filename=filename, bucket=bucket, key=key)
      raise ValueError(str_fmt.dedent().format(**fmt_vals))

~~~
stefco_
This is somewhat cleaner, and I also use this idiom when things get ugly with
the inline formatting shown above. But my point is that _none_ of these are
very elegant for an _extremely_ common use case. Throw this block in the
middle of some complex code with a few try/except and raise statements and it
still looks confusing. Having two extra temp variables and statements per
error in a function that's just doing control flow and wrapping unsafe code
can double your local variable count and number of statements across the whole
function. AFAIK, there has been no elegant solution to this common problem
until f-strings came around; the _only_ decently clean one is using printf-
style format strings with the old-style operator, but outside of terseness I
find it less readable.

------
voldacar
Python looks more and more foreign with each release. I'm not sure what
happened after 3.3 but it seems like the whole philosophy of "pythonic",
emphasizing simplicity, readability and "only one straightforward way to do
it" is rapidly disappearing.

~~~
teddyh
“I've come up with a set of rules that describe our reactions to technologies:

1\. Anything that is in the world when you’re born is normal and ordinary and
is just a natural part of the way the world works.

2\. Anything that's invented between when you’re fifteen and thirty-five is
new and exciting and revolutionary and you can probably get a career in it.

3\. Anything invented after you're thirty-five is against the natural order of
things.”

― Douglas Adams, The Salmon of Doubt

~~~
mehrdadn
I don't think it's just >35-year-olds who find what's going on in Python
against the natural order of things?

~~~
tachyonbeam
I'm 34 and I don't like this, so it's definitely not only those above 35.
Jokes aside, I would say I'm a minimalist and this is where my resistance
comes from. One of the things that I dislike the most in programming is
feature creep. I prefer smaller languages. I like the idea of having a more
minimal feature set that doesn't change very much. In a language with less
features, you might have to write slightly more code, but the code you write
will be more readable to everyone else. Said language should also be easier to
learn.

IMO, the more complex a system is, the more fragile it tends to become. The
same is true for programming languages. Features will clash with each other.
You'll end up having 10 different ways to achieve the same thing, and it won't
be obvious which direction to go.

Furthermore, I did my grad studies in compilers. I've thought about writing an
optimizing JIT for Python. I really feel like CPython is needlessly slow, and
it's kind of embarassing, in an age where single-core performance is reaching
a plateau, to waste so many CPU cycles interpreting a language. We have the
technology to do much better. However, the fact that Python is a fast moving
target makes it very difficult to catch up. If Python were a smaller, more
stable language, this wouldn't be so difficult.

~~~
RhodesianHunter
> In a language with less features, you might have to write slightly more
> code, but the code you write will be more readable to everyone else.

I disagree with this, which is precisely why I prefer feature rich languages
like Java or better yet Kotlin. It doesn't get much more readable than
something like:

    
    
      users.asSequence()
        .filter { it.lastName.startsWith("S") }
        .sortedBy { it.lastName }
        .take(3)
    

Now try writing that in Go or Python and compare the readability.

~~~
kragen
Python is a little more readable, but both Python and Kotlin are perfectly
clear in this case:

    
    
        sorted((u for u in users
               if u.last_name.startswith("S")),
               key=lambda u: u.last_name
        )[:3]
    

If last_name is a function, which it often would be in Python, it gets better:

    
    
        sorted((u for u in users
               if last_name(u).startswith("S")),
               key=last_name
        )[:3]
    

However, I think you probably got the sort key wrong if you're taking the
first three items of the result. Maybe you meant key=abuse_score,
reverse=True, or something.

~~~
elcritch
I disagree this python version is as readable and here’s why. It’s about as
many characters but more complex. The Kotlin version performs several distinct
actions, each being clear to its purpose. These actions have the same syntax
(eg requires less parsing effort). The Python version mixes at least 4
different language syntax/features, being list comprehension, if special form
in the list comprehension, keywords, and lambda functions.

On top of the lessened readability, the Kotlin version makes it very easy to
add, subtract, or comment out lines/actions which really helps when debugging.
The Kotlin version is almost identical in structure to how you’d do it in
Rust, Elixir, etc.

~~~
kragen
It's surprising to me that there are people who disagree with my opinion about
this, but it suggests that my familiarity with Python has damaged my
perspective. You're clearly much less familiar with Python (this code doesn't
contain any list comprehensions, for example), so I think your opinion about
readability is probably a lot more objective than mine.

~~~
DangitBobby
FWIW most of the programming I've ever done has been in Python, and while I
have no trouble understanding either snippet, I think that the Kotlin snippet
is much clearer in intent and structure.

~~~
kragen
I certainly didn't mean to imply that _only_ someone unfamiliar with Python
could prefer the Kotlin version! Perhaps you thought I meant that, but I
didn't.

------
raymondh
To me, the headline feature for Python 3.8 is shared memory for
multiprocessing (contributed by Davin Potts).

Some kinds of data can be passed back and forth between processes with near
zero overhead (no pickling, sockets, or unpickling).

This significantly improves Python's story for taking advantage of multiple
cores.

~~~
agent008t
Isn't that already the case?

I thought that when you use multiprocessing in Python, a new process gets
forked, and while each new process has separate virtual memory, that virtual
memory points to the same physical location until the process tries to write
to it (i.e. copy-on-write)?

~~~
jashmatthews
That's true but running VMs mutate their heaps, both managed and malloced. CoW
also only works from parent to child. You can't share mutable memory this way.

Empty space in internal pages gets used allocating new objects, refence counts
updated or GC flags get flipped etc, and it just takes one write in each 4kb
page to trigger a whole page copy.

It doesn't take long before a busy web worker etc will cause a huge chunk of
the memory to be copied into the child.

There are definitely ways to make it much more effective like this work by
Instagram that went into Python 3.7: [https://instagram-engineering.com/copy-
on-write-friendly-pyt...](https://instagram-engineering.com/copy-on-write-
friendly-python-garbage-collection-ad6ed5233ddf)

------
londons_explore
I long for a language which has a basic featureset, and then "freezes", and no
longer adds any more language features.

You may continue working on the standard library, optimizing, etc. Just no new
language features.

In my opinion, someone should be able to learn all of a language in a few
days, including every corner case and oddity, and then understand _any_ code.

If new language features get added over time, eventually you get to the case
where there are obscure features everyone has to look up every time they use
them.

~~~
colechristensen
This is why a lot of scientific code still uses fortran, code written several
decades ago still compiles and has the same output.

How long has the code which was transitioned to python lasted?

~~~
airstrike
> How long has the code which was transitioned to python lasted?

A long time. 2to3 was good for ~90% of my code, at least

~~~
colechristensen
Good for 90% of your code is not equivalent to getting precisely the same
results from unmodified code written in the 80s.

~~~
mixmastamyk
More likely to mean 90% of projects, not 90% of each file, which would mean
that every one was broken.

------
stakhanov
Speaking as someone who has written Python code almost every day for the last
16 years of my life: I'm not happy about this.

Some of this stuff seems to me like it's opening the doors for some
antipatterns that I'm consistently frustrated about when working with Perl
code (that I didn't write myself). I had always been quite happy about the
fact that Python didn't have language features to blur the lines between
what's code vs what's string literals and what's a statement vs what's an
expression.

~~~
sametmax
F-strings have appeared 2 versions ago. All in all, the feedback we have has
been overwhelmingly positive, including on maintenance and readability.

~~~
sleavey
I love f-strings. I just wish tools like pylint would shut up when I pass
f-strings to the logging module. I as the developer understand and accept the
extra nanosecond of processor time to parse the string that might not be
logged anywhere!

~~~
orf
It's not always a nanosecond, some string representations can take a while to
create. In poorly coded Django models they could involve a trip to the
database.

~~~
mixmastamyk

        if logger.isEnabledFor(logging.DEBUG):
            logger.debug(f'{expensive_func()}')

~~~
orf
I hope that’s a joke, because that is a verbose and ridiculous way of
duplicating the work that the logging module does, while also making the code
less readable and maintainable!

~~~
mixmastamyk
That’s how you defer an expensive function. The fstring part is the joke.

------
stefco_
There's a lot of talk in this thread about Python going down-hill and becoming
less obvious/simple. I rather like modern python, but I agree that some
features (like async/await, whose implementation fractures functions and
libraries into two colors [0]) seem like downgrades in "Pythonicity".

That said, I think some things have unquestionably gotten more "Pythonic" with
time, and the := operator is one of those. In contrast, this early Python
feature (mentioned in an article [1] linked in the main one) strikes me as
almost comically unfriendly to new programmers:

> Python vowed to solve [the problem of accidentally assigning instead of
> comparing variables] in a different way. The original Python had a single
> "=" for both assignment and equality testing, as Tim Peters recently
> reminded him, but it used a different syntactic distinction to ensure that
> the C problem could not occur.

If you're just learning to program and _know nothing_ about the distinction
between an expression and a statement, this is about as confusing as shell
expansion (another context-dependent syntax). It's _way_ too clever to be
Pythonic. The new syntax, though it adds an extra symbol to learn, is at least
100% explicit.

I'll add that := fixes something I _truly hate_ : the lack of `do until` in
Python, which strikes me as deeply un-Pythonic. Am I supposed to break out of
`while True`? Am I supposed to set the variable before _and_ at the tail of
the loop (a great way to add subtle typos that will cause errors)? I think it
also introduces a slippery slope to be encouraged to repeat yourself: if
assigning the loop variable happens twice, you might decide to do something
funny the 2:Nth time to avoid writing another loop, and that subtlety in loop
variable assignment can be very easy to miss when reading code. There is no
general solution I've seen to this prior to :=. Now, you can write something
like `while line := f.readline()` and avoid repetition. I'm very happy to see
this.

[0] [https://journal.stuffwithstuff.com/2015/02/01/what-color-
is-...](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-
function/)

[1] [https://lwn.net/Articles/757713/](https://lwn.net/Articles/757713/)

[edit] fixed typos

~~~
owlowlowls
>I'll add that := fixes something I truly hate: the lack of `do until` in
Python, which strikes me as deeply un-Pythonic. Am I supposed to break out of
`while True`? Am I supposed to set the variable before and at the tail of the
loop (a great way to add subtle typos that won't cause errors)?

This is relevant to what I've been doing in OpenCV with reading frames from
videos! In tutorial examples on the web, you'll see exactly the sort of
pattern that's outlined in the PEP 572 article.

>line = f.readline()

>while line:

> ... # process line

> line = f.readline()

Just, replace readline() with readframe() and the like. So many off-by-one
errors figuring out when exactly to break.

~~~
Aramgutang
That example can also be tackled with Python's little-known feature of calling
the built-in `iter` with a second argument:

> for line in iter(f.readline, ''):

> ... # process line

See:
[https://docs.python.org/3/library/functions.html#iter](https://docs.python.org/3/library/functions.html#iter)

~~~
jl2718
This is useful, but two totally different functions with the same name
distinguished only by the presence, not even the value of an unused argument.
Where else does anything like this exist in the language? Seems problematic.

~~~
dragonwriter
> This is useful, but two totally different functions with the same name
> distinguished only by the presence, not even the value of an unused
> argument.

The sentinel argument is not unused.

> Where else does anything like this exist in the language?

In the narrow sense of “in the language” (builtins only), it's unique. There
may be other examples in stdlib. That aside, functions that do a descriptively
similar thing (i.e.,“build an iterator”) but where the details of how they do
it differ significantly by aritt aren't weird among programming languages
generally; don't see why it would be particularly problematic.

------
vesche
Was really hoping to see multi-core in 3.8, looks like we'll be waiting until
3.9

[https://www.python.org/dev/peps/pep-0554/](https://www.python.org/dev/peps/pep-0554/)

[https://github.com/ericsnowcurrently/multi-core-
python/wiki](https://github.com/ericsnowcurrently/multi-core-python/wiki)

~~~
Stubb
A map() function that isn't just an iterated fork() would be glorious. Let me
launch a thread team like in OpenMP to tackle map() calls containing SciPy
routines and I'll be unreasonably happy.

------
sleavey
Without wanting to ignite a debate about the walrus operator (and having not
read any of the arguments), I can guess why there was one. It's not clear to
me what it does just from reading it, which was always one of Python's
beginner-friendlinesses.

~~~
coldtea
> _It 's not clear to me what it does just from reading it_

How isn't it entirely obvious? := is the assignment operator in tons of
languages, and there's no reason not to have assignment be an expression (as
is also the case in many languages).

~~~
txcwpalpha
> := is the assignment operator in tons of languages

It is? Which ones? Other than Go, I can not think of a single language that
has ":=" as an operator. Java does not, JavaScript does not, C/C++ do not,
Ruby does not, I don't think PHP does, Erlang/Elixir do not, Rust does not...
(I could be wrong on these, but I've personally never seen it in any of these
languages and I can't find any mention of it in these languages' docs).

I tried looking around the internet at various popular programming languages
and the only ones I could find that use ":=" are: Pascal, Haskell (but it's
used for something else than what Python uses it for), Perl (also used for
something else), and Scala (but in Scala it isn't officially documented and
doesn't have an 'official' use case).

I don't have a strong opinion about ":=" in Python but I do agree that it's
unintuitive and thus not very "Pythonic".

~~~
hathawsh
See:
[https://en.wikipedia.org/wiki/Assignment_(computer_science)#...](https://en.wikipedia.org/wiki/Assignment_\(computer_science\)#Notation)

At least 18 prominent languages use that syntax.

~~~
megla_
I genuinely haven't heard of 13 of those and I'd say I'm quite interested in
learning about languages. The few that I know, were just briefly mentioned by
a professor, so I don't know anything apart from the name.

What qualifies as prominent to you? How old are you? On Tiobe Index only
Pascal and Go are in the first 50, while half of them aren't even listed in
the first 100. Sure they're important and had an impact on new languages, but
most of them were made ~50 years ago.

So many new languages were developed since then, which are far more useful and
prominent than these legacy ones. If almost none of the _modern_ ones have
implemented it so far, is it really that useful/needed?

~~~
coldtea
> _I genuinely haven 't heard of at least a third of them and I'd say I'm
> quite interested in trying new (and possibly unusual) languages._

But perhaps not as interested in trying old and significant languages?

> _What qualifies as prominent to you? On Tiobe Index only Pascal and Go are
> in the first 50, while half of them aren 't even listed in the first 100.
> Sure they're important and had an impact on new languages, but most of them
> were made ~50 years ago._

Well, Lisp was made 60+ years ago, and C 50 years ago, so?

Besides Go, Smalltalk, Ada, and Pascal would be significant languages in any
book, and I'd add Simula, Oberon, Eiffel, and Dylan to the list.

Seriously, if one haven't at least heard of Simula (the father language of OO)
I'm not sure how qualified they are to pass PL judgement.

~~~
megla_
_> Well, Lisp was made 60+ years ago, and C 50 years ago, so?_

Well, they're still seeing widespread use, that's why they're on Tiobe, while
others faded into obscurity. Those languages are historically significant, but
nowadays they're basically useless apart from scientific use and maintaning
old software.

Maybe you should understand that the majority of programmers are younger than
Python and don't study the same material they did 30 years ago, because a
whole lot of history happened in that time. Also I'm not sure how not knowing
about Simula makes me unqualified for anything.

I've noticed, not just in this reply, but in all of your comments; your
condescending tone and indirect addressing make you seem like an unpleasant
person.

Using these qualities makes one seem like some stuck-up pseudointellectual
boomer.

------
gclaugus
Walrus operator looks like a great addition, not too much syntax sugar for a
common pattern. Why were folks arguing about it?

~~~
chrisseaton
I don't know how you read it - 'if x is assigned the value y'? Most other
things in Python can just be read out loud.

~~~
thaumasiotes
Read it "if y" \- that's what's being tested.

The walrus simultaneously names the value being tested so you can refer to it
within the condition; it's sort of the inverse of Perl code using $_. So
instead of

    
    
        if (do_something()) {
            act_on($_);
        }
    

you have

    
    
        if placeholder := do_something():
            act_on(placeholder)
    

But when reading aloud, however you'd read the perl will flow as more natural
english. "If the string contains z, do something with it".

If you really want to read the Python as it's written, it corresponds to the
second of these sentences:

\- If the substring from 3 to 5 is "fg", crash.

\- If _variable_name_ , the substring from 3 to 5, is "fg", crash.

~~~
txcwpalpha
>Read it "if y" \- that's what's being tested.

Once way to test if this works is to take the code, read it aloud, and then
use the read-aloud version to rewrite the code. If you don't have a high
degree of certainty that you end up with the same code, something has failed
along the way.

In this case, if I take "if x:= y()" and read it aloud as "if y", I think the
vast majority of people would translate that to code as "if y():", which isn't
the same thing.

~~~
thaumasiotes
You will end up with the same code under two conditions:

1\. You read more than one line of code.

2\. Executing the code in the conditional more than once doesn't matter.

If you meet those two assumptions, then the reading I suggested will transform
this

    
    
        if x := y():
            act_on(x)
    

into this

    
    
        if y():
            act_on(y())
    

which is, in fact, the same thing.

~~~
txcwpalpha
That second point is quite an assumption to make. If it's okay to end up with
the second one of your examples (without the ":=" operator), why did we need
to add the walrus opeprator at all?

And if you're referring to this statement from your original comment:

> If variable_name, the substring from 3 to 5, is "fg", crash.

I don't find this to be a clear statement at all. If I read this aloud to any
of my programming students, I doubt any of them would be able to decipher it
into _any_ code, let alone the code string which you've suggested.

~~~
thaumasiotes
> If it's okay to end up with the second one of your examples (without the
> ":=" operator), why did we need to add the walrus operator at all?

A couple of reasons:

\- The walrus eliminates a line of code in the very common scenario where you
would prefer not to recalculate (or retype) y().

\- The walrus makes it easy to avoid recalculating y() in the less common
scenario where you need to avoid doing that.

> I don't find this to be a clear statement at all.

Nonetheless, it is the normal English syntax used to name something and then
immediately define the meaning of that name. If you want to map the Python
structure to an equivalent English structure, that is the equivalent English
structure. ("Thomas Jefferson, the third president, was a man about whom much
can be said.") If you want to map the Python code to an English statement of
what the code does, use the reading I first suggested, "if y(), do something
with it". If you want to dictate Python code to an English speaker, use the
reading "if x colon-equals y()".

So let me ask you: is the problem you'd like to solve "I want to understand
what this code does", is it "I like thinking about English grammar", or is it
"I'm too busy to type my own code; that's what my secretary is for"?

~~~
txcwpalpha
>So let me ask you: is the problem you'd like to solve "I want to understand
what this code does", is it "I like thinking about English grammar", or is it
"I'm too busy to type my own code; that's what my secretary is for"?

The problem that has _already_ been solved by every version <3.8 of Python,
and that I would hate to see become un-solved by Python in the future, is that
Python's greatest attribute, by _far_ , is that it has always stuck to a code
of being "pythonic", increasing accessibility, readability, and teachability
to wide audiences. A hugely significant reason for Python's incredible rise in
popularity and its use today is _specifically because_ of it's readability. As
much as it gets meme'd about, the fact that Python pseudocode is so close to
real Python code is an _enormous_ boon for the language.

I teach programming at a university level, and Python is _the_ go-to, default
language to teach programming. Dictating code so that it can be discussed in a
classroom setting is very important, and as I mentioned before, your
suggestion for reading it aloud just wouldn't cut it. Python is also _the_ go-
to, default language for programming-adjacent fields like data science, a lot
of statistics, and every other non-programming-but-still-IT field. And again,
this is because the people in these fields _love_ the fact that, even with
zero previous programming experience, they can look at or hear a piece of code
and almost immediately understand what it is doing.

Python's strict adherence to being "pythonic" is hugely responsible for an
_entire generation_ of programmers, and hopefully will continue to be pythonic
enough to continue lowering the barriers of entry to future programmers. I get
that many seasoned developers are adopting an "well I got mine" attitude and
don't care if future developers have a harder time learning the trade, but I
personally find that to be very selfish, and I would hate to see future
generations of programmers suffer just because the current generation
apparently can't be arsed to do something like write one single, short extra
line of code every now and then.

~~~
thaumasiotes
> I would hate to see future generations of programmers suffer just because
> the current generation apparently can't be arsed to do something like write
> one single, short extra line of code every now and then.

Every now and then? If you look at the example in the post, you'll see it
saving one line of code _per branch of the if-elif ladder_.

Python code is characteristically tall, and this saves a lot of lines, and it
saves them all in exactly the same way.

~~~
txcwpalpha
>Every now and then?

Yes, every now and then. The example in this post is an edge case. _And even
if it wasn 't_, the walrus operator saved a grand total of _three_ (3!)
keystrokes/characters per conditional in the example. "saves a lot of lines"
is hyperbole. If the goal of this was saving programmer time or reducing the
amount of code, the walrus operator should have been one of the absolute last
things in Python to change.

Even just within this HN thread, even the staunchest proponents of this change
are admitting that it is only going to be used sparingly, and at best saves a
handful of lines of code per project.

If you're seriously telling me that saving a measly three keystrokes is more
important to you than maintaining the pythonic philosophy that has made Python
successful for decades, I can say nothing else other than that I strongly
encourage you to reevaluate your priorities.

edit: I actually did the math wrong. It's only two (2!) keystrokes saved per
conditional.

------
traderjane
[https://docs.python.org/3.8/whatsnew/3.8.html](https://docs.python.org/3.8/whatsnew/3.8.html)

~~~
ehsankia
I don't know why the downvotes, but I personally much prefer this to the
editorialized and incomplete list in the current list.

Looking at the module changes, I think my top pick is the changes to the
`math` module:

> Added new function math.dist() for computing Euclidean distance between two
> points.

> Added new function, math.prod(), as analogous function to sum() that returns
> the product of a ‘start’ value (default: 1) times an iterable of numbers.

> Added new function math.isqrt() for computing integer square roots.

All 3 are super useful "batteries" to have included.

------
mottosso
Very much looking forward to assignment expressions! It's something I've
wanted to do every so often, only to realise that you can't. A worthy addition
to the already intuitive Python language.

~~~
tomd3v
Seriously. I recently came from PHP, and this is one feature I've been missing
quite often and a lot.

------
Alex3917
Have there been any performance benchmarks done on Python 3.8 yet? I'd be
interested in seeing how it compares to 3.6 and 3.7, but haven't seen anything
published.

~~~
gonational
Absolutely this.

I think that the most important thing Python can do in each release is to
improve performance, incrementally.

------
president
Anyone else think the walrus operator is just plain ugly? There is a certain
aesthetic quality that I've always appreciated about the Python language and
the walrus operator looks like something straight out of Perl or Shell.

~~~
brown9-2
Looks pretty normal if you do any amount of Go.

------
gonational
I recommend a talk from Pycon 2019, wherein Dustin Ingram explains PEP-572
(aka the Walrus Operator) better than I’ve seen done elsewhere.

IMHO, the usefulness of this new operator outweighs the slight learning curve
required to get past the awkwardness you will experience when you are first
acquainted to it.

Here is that talk:

[https://youtu.be/6uAvHOKofws](https://youtu.be/6uAvHOKofws)

------
ohazi
Also type hints for dictionaries with fixed keys:

[https://www.python.org/dev/peps/pep-0589/](https://www.python.org/dev/peps/pep-0589/)

I know it's almost always better to use objects for this, but tons of code
still uses dictionaries as pseudo-objects. This should make bug hunting a lot
easier.

~~~
lxmcneill
Huh, was totally unaware of this. For me this has good implications for
ingesting CSVs/.xlsx to dicts. Clean-ups / type hinting is required at times
for dirtier documents.

------
lordnacho
Gotta ask how many of these changes are actually reflective of changing
environments.

I could see with c++ that between 2003 and 2014 a fair few underlying machine
things were changing and that needed addressing in the language.

But Python is not quite as close to the machine, and I don't see how something
like the walrus is helping much. If anything it seems like you'd scratch your
head when you came across it. And for me at least one of the main attractions
of python is you're hardly ever surprised by anything, things that are there
do what you guessed, even if you hadn't heard of them. Function decorators for
instance, you might never have seen one but when you did you knew what it was
for.

Same with the debug strings. That seems to be a special case of printing a
string, why not leave it at that? I'm guessing a lot of people don't ever read
a comprehensive python guide, what are they going to do when they see that?

~~~
thaumasiotes
> I'm guessing a lot of people don't ever read a comprehensive python guide,
> what are they going to do when they see that?

My guess would be "run it and see what it does".

------
stuaxo
It seems like the PEP0505 for None aware operator is delayed indefinitely.

It would be great if there was more momentum on this again, as it would be
helpful in all sorts of places.

[https://www.python.org/dev/peps/pep-0505/](https://www.python.org/dev/peps/pep-0505/)

------
Waterluvian
The lack of the "nursery" concept for asyncio really sucks. Originally I heard
it was coming in 3.8. Right now asyncio has this horrible flaw where it's
super easy to have errors within tasks pass silently. It's a pretty large foot
gun.

~~~
sametmax
You can code your own wrapper for this.

Like [https://github.com/Tygs/ayo](https://github.com/Tygs/ayo)

It's not as good as having it in the stdlib, because people can still call
ensure_future and not await it, but it's a huge improvement and completly
compatible with any asyncio code.

~~~
Waterluvian
Yup for sure. My complaint is part ergonomics of boilerplate, part this really
burned me bad and no stdlib documentation warns you upfront about it. So many
hours of headscratching.

------
preommr
":=" is a fairly common operator symbol that I've seen used in other
programming languages (e.g. Golang) and in mathematics. But I've never seen it
called the "walrus" operator. Its fitting and memorable though, I like it.

------
BuckRogers
The problem with modern Python is that it's trying to recreate C# or Java.
Which leaves it with nothing, because it'll only end up an inferior version of
the languages/platforms of which it's attempting to duplicate.

When I was into Python, I liked it because it was a tighter, more to the
basics language. Not having 4 ways to format strings and so forth. I don't
think Python can defeat Java by becoming Java. It'll lose there due to
multiple disadvantages. The way Python "wins" (as much as it could at least),
is focusing on "less is more". They abandoned that a while ago.

My vision of a language like Python would be only 1-way to do things, and in
the event someone wants to add a 2nd way, a vote is taken. The syntax is
changed, and the old bytecode interpreter handles old scripts, and scripts
written with the latest interpreter's bytecode only allows the new syntax. For
me that's the joy of Python.

I think a lot of people wanted Python's original vision, "one way to do
things". If I want feature soup, I'll use what I program in daily. Which I do
want feature soup by the way, I just have no need to replace it with another
"feature soup" language like Python turned into because it's inferior on
technical and for me, stylistic levels.

~~~
orangecat
_My vision of a language like Python would be only 1-way to do things, and in
the event someone wants to add a 2nd way, a vote is taken._

By that standard, the walrus operator is not only acceptable but essential.
Right now there are at least 3 ways to process data from a non-iterator:

    
    
      # 1: loop condition obscures what you're actually testing
      while True:
          data = read_data()
          if not data:
              break
          process(data)
    
      # 2: 7 lines and a stray variable
      done = False
      while not done:
          data = read_data()
          if data:
              process(data)
          else:
              done = True
    
      # 3: duplicated read_data call
      data = read_data()
      while data:
          process(data)
          data = read_data()
    

There's too many options here, and it's annoying for readers to have to parse
the code and determine its actual purpose. Clearly we need to replace all of
those with:

    
    
      while (data := read_data()):
          process(data)
    

Yes, I'm being a bit snarky, but the point is that there is never just one way
to do something. That's why the Zen of Python specifically says one "obvious"
way, and the walrus operator creates an obvious way in several scenarios where
none exist today.

~~~
BuckRogers
I have no opinion on this new feature, so I can't really engage in that debate
over it. I still see no reason for smaller languages like Python to not
operate as a true direct democracy. There's clearly frustration over the
leadership in the PSF, so much so that Guido capitulated under the stress.

------
RcouF1uZ4gsC
I find the different philosophies of languages amazing.

Just recently 'Declined Proposal: A built-in Go error check function, “try”'
[https://news.ycombinator.com/item?id=20454966](https://news.ycombinator.com/item?id=20454966)
made the front page, explaining how a controversial potential Go feature was
being declined early.

Python on the other hand, went ahead with what seems to be a proposal at least
as controversial as 'try' in Go.

------
philsnow
I noticed some changes to pickle; do people still use pickle for Real Work?

Potential vulnerabilities aside, I got bitten by some migration issue back in
the 2.2 to 2.4 transition where some built-in types changed how they did their
__setstate__ and __getstate__ (iirc) and that caused objects picked under 2.4
to not unpickle correctly under 2.2 or something like that. After that I never
wanted to use pickle in production again.

~~~
brilee
Pickle is only guaranteed to work within python versions and shouldn't be used
as a long-term data storage strategy. It's really intended for quick-n-dirty
serialization, or for multiprocessing communication, where the objects are
ephemeral.

------
rcfox
I feel like I've been seeing a lot of these almost identical articles pop up
all over. Walrus operator, f-string equals, positional-only arguments, _yawn_.
None of that is really going to change your life.

There's a bunch of changes in the official "what's new" doc that I think are
more interesting:

[https://docs.python.org/3.8/whatsnew/3.8.html](https://docs.python.org/3.8/whatsnew/3.8.html)

* Run-time audit hooks, to see if your modules are making network requests, etc.

[https://www.python.org/dev/peps/pep-0578/](https://www.python.org/dev/peps/pep-0578/)

[https://tirkarthi.github.io/programming/2019/05/23/pep-578-o...](https://tirkarthi.github.io/programming/2019/05/23/pep-578-overview.html)

* multiprocessing SharedMemory for fast data sharing between processes

[https://docs.python.org/3.8/library/multiprocessing.shared_m...](https://docs.python.org/3.8/library/multiprocessing.shared_memory.html)

* Duck-typing for the static annotation checkers

[https://www.python.org/dev/peps/pep-0544/](https://www.python.org/dev/peps/pep-0544/)

* Literal checking for the static annotation checkers. ie: It's not enough to check that you're passing a string for the mode in open(), you want to check that it's 'r' or 'w', etc.

[https://www.python.org/dev/peps/pep-0586/](https://www.python.org/dev/peps/pep-0586/)

* The compiler now produces a SyntaxWarning when identity checks (is and is not) are used with certain types of literals (e.g. strings, ints). These can often work by accident in CPython, but are not guaranteed by the language spec. The warning advises users to use equality tests (== and !=) instead.

* A bunch of speed and memory optimizations:

\- "Sped-up field lookups in collections.namedtuple(). They are now more than
two times faster, making them the fastest form of instance variable lookup in
Python."

\- "The list constructor does not overallocate the internal item buffer if the
input iterable has a known length (the input implements __len__). This makes
the created list 12% smaller on average."

\- "Doubled the speed of class variable writes."

\- "Reduced an overhead of converting arguments passed to many builtin
functions and methods. This sped up calling some simple builtin functions and
methods up to 20–50%."

------
tasty_freeze
I'm all in favor of the walrus operator for the for loop, but the first
example given to justify it is code I'd never write. The first if does a
return, so there is no need for the else: and indentation. I'm sure there are
other code examples that would justify it, but this one is unconvincing.

~~~
duckerude
The return statements make it a poor example. There's an example from the
standard library in the PEP that has a similar shape:

    
    
      reductor = dispatch_table.get(cls)
      if reductor:
          rv = reductor(x)
      else:
          reductor = getattr(x, "__reduce_ex__", None)
          if reductor:
              rv = reductor(4)
          else:
              reductor = getattr(x, "__reduce__", None)
              if reductor:
                  rv = reductor()
              else:
                  raise Error(
                      "un(deep)copyable object of type %s" % cls)
    

Becomes:

    
    
      if reductor := dispatch_table.get(cls):
          rv = reductor(x)
      elif reductor := getattr(x, "__reduce_ex__", None):
          rv = reductor(4)
      elif reductor := getattr(x, "__reduce__", None):
          rv = reductor()
      else:
          raise Error("un(deep)copyable object of type %s" % cls)

------
Animats
The title made me think "Be afraid. Be very afraid". But it's all little
stuff.

Unchecked type annotations remain the worst addition since 3.0. Actual typing
might be useful; it allows optimizations and checking. But something that's
mostly a comment isn't that helpful.

~~~
snicker7
Both static checking and compilation can be implemented using third party
libraries. I think projects like mypyc could be a real game-changer.

------
Grue3
>Python 3.8 programmers will be able to do: print(f'{foo=} {bar=}')

Ugh, how did this get approved? It's such a bizarre use case, and debugging by
print should be discouraged anyway. Why not something like debug_print(foo,
bar) instead (because foo and bar are real variables, not strings)?

~~~
jimktrains2
I don't understand why you think print or log debugging is inherently bad.

Also, it's part of the format string and not a special print function so that
it can be used for logs and other output as well, not just the console.

~~~
Grue3
>I don't understand why you think print or log debugging is inherently bad.

I use it myself all the time, but it just shows the weakness of the tooling
that people have to resort to such measures. Fortunately, some people are
working on it [1].

>Also, it's part of the format string and not a special print function so that
it can be used for logs and other output as well, not just the console.

Since print (and hypothetical debug_print) are no longer statements like in
2.x, there's nothing preventing them from returning the string that's supposed
to be printed. For example print's keyword file is sys.stdout by default. Why
not borrow from Common Lisp's format and make it return the string if
file=None is passed? Then you could do logging.warning(debug_print('Unusual
situation', foo, bar, file=None)) and it would print "WARNING: Unusual
situation: foo=foo_value, bar=bar_value" to the logs. It's so much clearer.

[1] [https://github.com/cool-RR/pysnooper](https://github.com/cool-
RR/pysnooper)

~~~
kazinator
That's poor compared to just logging.warning('Unusual situation', foo, bar).

I.e. since print isn't a statement, you can give other functions the same
argument conventions that it has, instead of making the programmer use a
string-generating shim.

Speaking of Common Lisp, it has functions other than _format_ which take
_format_ arguments.

    
    
      $ clisp -q
      [1]> (error "value of ~s should be less than ~s" 'bloop 42)
    
      *** - value of BLOOP should be less than 42
      The following restarts are available:

~~~
Grue3
I don't expect logging.warning to change in future versions of Python
(currently it substitutes extra args as % operator, so you'd have to write
logging.warning('Unusual situation foo=%s, bar=%s', foo, bar)) since it would
unnecessarily break too much code. But since print doesn't return any usable
value, it can be easily updated to return a useful value.

------
antpls
At first, after reading the comments and before reading the article, I thought
everyone was just casually bashing because of change. But just look at this :

def fun(a, b, /, c, d, *, e, f):

or

print(f'{now=} {now=!s}')

and guess what it does before actually reading the article.

Worst, the rationales of the PEPs are weak, presenting improvement for
"performances" or enforcement of API because of low level stuff as C.

Back when I was 18 years old, Python was high level, rules were simple with
only one way of doing things and performances weren't a concern, because you
would just use the right language for the right task. There was no enforcement
and you could always hack a library to your liking. Python now is getting
closer to what Perl looked to me 10 years ago, trying to optimize stuff it
shouldn't

------
sandGorgon
Does anyone know the status of pep-582 :
[https://www.python.org/dev/peps/pep-0582/](https://www.python.org/dev/peps/pep-0582/)

It's still marked as a 3.8 target

~~~
mixmastamyk
Too late I think.

------
atiredturte
I feel like walrus operators, while a cool construct, are at odds with "The
Zen of Python".

Specifically "There should be one -- and preferably only one --obvious way to
do it."

If this was any other language, the addition would be welcome, but I feel that
the walrus operator fundamentally disagrees with what python is about.

It's not about terseness and cleverness, it's about being clear, and having
one way to do things (Unless you are Dutch).

[https://www.python.org/dev/peps/pep-0020/](https://www.python.org/dev/peps/pep-0020/)

------
Myrmornis
I believe that I hit places where I'd use the walrus about once every few
hundred lines of python, so I do see a use for it. OTOH I am worried that it
makes the language harder to understand for beginners, and that is a very
important role Python plays in the world of programming languages.

The abbreviated f-string syntax looks weird and kinda wrong to me. But then
I'm not even sure I've got comfortable yet with the object field
initialization shortcuts in Javascript and Rust (where you also get to omit
stuff to avoid repeating yourself).

------
wil421
>Debug support for f-strings.

F strings are pretty awesome. I’m coming from JavaScript and partially java
background. JavaScript’s string concatenation can become too complex and I
have difficulty with large strings.

>Python 3.8 programmers will be able to do: print(f'{foo=} {bar=}')

Pretty cool way to help with debugging. There are so many times, including
today, I need to print or log some debug string.

“Debug var1 ” + var1 + “ debug var2” + var2...and so on. Forgot a space again.

~~~
joaolvcm
By the way, this has nothing do with f strings but for debugging JavaScript
you can do something like

console.log({var1,var2,var3});

And the logged object will get created with the variables content and the
variable nem as key, so it will get logged neatly like

{var1: "this is var1", var2: 2, var3: "3"}

------
punnerud
Is there anything similar to BabelJS for Python? Now after 3.8 I start to feel
there are a need for a tool like that.

More compact code at the cost of higher learning curve.

------
nickthemagicman
Its kind of amazing to me switching from PHP/Ruby to Python, that things like
f strings and walrus operators are just now being added to python.

------
jasonrhaas
The walrus operator does not feel like Python to me. I'm not a big fan of
these types of one liner statements where one line is doing more than one
thing.

It violates the philosophies of Python and UNIX where one function, or one
line, should preferably only do one thing, and do it well.

I get the idea behind the :=, but I do think it's an unnecessary addition to
Python.

~~~
fatbird
The unix philosophy of simplicity was on a per tool basis, not function or
line of code. The walrus operator is Python version of what we can do now in C
or in JS, doing plain assignment in an expression while evaluating it for
truthiness. And more often than not, the point of that single-purposeness in
Unix is so you can chain a bunch of piped commands that result in a perl-like
spaghetti command that's three terminal widths long.

~~~
jstimpfle
> while evaluating it for truthiness

no, it evaluates to the left side's value after assignment

~~~
fatbird
Well, yes, that's right: the return value of an assignment expression is the
left side's value, which is what makes the assignment/evaluation work.

------
ProjectBarks
The changes to f-strings just seems like a step in the wrong direction. Don't
make the string content implicit!

~~~
strictfp
Also, why abandon printf-style? All languages tend to converge to printf over
time, it's simply the most tried and tested model out there!

~~~
joshuamorton
javascript, python, rust, etc. don't use printf style, but instead use the {}
style.

~~~
strictfp
Yeah, well Rust isn't exactly a success story in that regard if you ask me. A
couple of weeks ago I tried to figure out how to format a float properly in
Rust, and the way they made it work is a lot worse than straight up printf-
style if you ask me.

------
mcdermott
Python has "jumped the shark" for me. Python is no longer Pythonic (the
"import this", zen of python easter egg should be removed). It's lost it way
and is TIMTOWTDI now, heading for that Perl 6 brass ring. Golang is now the
Pythonic language.

------
tasubotadas
I'll just put a reminder here that it's the year 2019 and AMD and Intel has
10-core CPUs while Python is still stuck with GIL ¯\\_(ツ)_/¯

~~~
ben509
It's the current year!

This is slated for 3.9:
[https://www.python.org/dev/peps/pep-0554/](https://www.python.org/dev/peps/pep-0554/)

~~~
tasubotadas
I think we both know that it's a poor substitute for proper threading.

------
jpetrucc
I love the f-strings and the new enhancements, but I'm still skeptical about
the walrus operator and the positional argument change.

------
outerspace
Does it make sense to use := everywhere (can it be used everywhere?) instead
of just in conditionals? Just like Pascal.

~~~
DonHopkins
About as much sense as it makes to use ; after every Python statement. Just
like Pascal.

(Yeah I know, ; is a statement separator, not a statement terminator in
Pascal.)

As long as you're being just like Pascal, did you know Python supported
Pascal-like "BEGIN" and "END" statements? You just have to prefix them with
the "#" character (and indent the code inside them correctly, of course). ;)

    
    
        if x < 10: # BEGIN
            print "foo"
        # END

------
singularity2001
Do parsers of previous pythons emit warnings: "this feature is not available
in pythons 3.3 3.4 3.5 etc" ?

~~~
ben509
No, just a SyntaxError.

Generally, library authors won't be able to use it if they want to support
many versions; same as with f-strings.

------
vkaku
That walrus operator has given me exactly what I wanted from C. Although I'd
have preferred:

if val = expr():

~~~
hyperion2010
That particular version opens the way for massive typo footguns and results in
the insanity of defensive programming patterns like yoda expressions.

~~~
vkaku
Well, for those used to those expressions, it definitely helps write that code
with one line lesser (assignment by itself).

Most likely, the assigned value is stored for use in one of the conditionals,
so it really doesn't change any of that.

Let's also understand that we are dealing with a decorated assignment here, so
a = (b = c) should be no different from evaluating (b = c). It's not
complicated, the way I at least look at it.

~~~
vkaku
But now I see your point. A language must not give beginners an option to
shoot themselves in the foot.

------
ihuman
How come they are using a new := operator instead of using equals?

~~~
dragonwriter
Which equals?

= (existing) is statement assignment

== (existing) is expression equality

:= (new) is expression assignment

~~~
ihuman
Just 1 equals. It could assign a statement to a variable, and return that
value/variable to the if statement to check for truthyness

    
    
       a=42
       if b = a:
         print(b)
       else:
         print("no")
    

Would print "42". It works in C

    
    
      int a,b;
      a=42;
      if(b=a){
        printf("%d\n",b);
      } else {
        printf("no\n");
      }

~~~
magicalhippo
It works in C, and have caused countless bugs in C (and C++).

So much so that many have adopted the rule that the variable goes on the
right, "if 42 = b", to make sure the compiler barfs when you intended to write
"if b == 42".

With := it's less likely that mistake is made. I also find it visually more
distinct, so easier to parse, but that might be very subjective.

------
dirkg
What Python needs is a better lambda syntax similar to JS and true anonymous
multiline functions. Defining and using lambdas in Python feels very
unpythonic, this is something JS gets perfectly.

Also fix the GIL.

------
RocketSyntax
Isn't the walrus just like a case statement?

------
musicale
All I care about is allowing a print statement in addition to the print
function. There's no technical reason why both can't coexist in a perfectly
usable manner.

~~~
mixmastamyk
Try an editor snippet like I did years ago. It's even shorter to type:

    
    
        pr<TAB>  -->  print(" ")
                      #      ^ cursor

------
dec0dedab0de
I dont like the positional only arguments..

Really, I dont like anything that trys to force a future developer into using
your code the way you expect them to.

~~~
nneonneo
One of the use-cases for positional-only arguments strikes me as being very
sensible:

    
    
        def my_format(fmt, *args, **kwargs):
            ...
            fmt.format(*args, **kwargs)
    

suffers from a bug if you want to pass fmt as a keyword argument (e.g.
`my_format('{fmt}', fmt='int')`). With positional-only arguments that goes
away.

You could always force developers into using your code the way you expect by
parsing args/kwargs yourself, so it's not like this really changes anything
about the "restrictiveness" of the language.

------
stesch
No new way to format a string?

------
Areading314
Very much seems like perlification, and we all know what happened to Perl.

Although that being said I always really liked Perl

~~~
lizmat
Perhaps it's more Perl 6-ification?

------
ggm
And the GIL...

------
apalmer
2nd 2

------
apalmer
3r

------
apalmer
3

------
terminalhealth
tl;dr: Computation is being compressed ever more

------
patientplatypus
Personally, I vote against the walrus. Code complication for a limited set of
use cases. Boo, bad walrus.

------
GrumpyNl
Why elif en not juste elseif?

~~~
mehrdadn
Or just else if... but honestly elif is easiest to type and it's not hard to
understand.

~~~
reallydude
Hard is being used in a "type the keys" sense. It's more complexity to borrow
idioms from languages then slightly change the syntax (PHP!), which isn't
necessary. Like most languages, choices are made without evidence (but plenty
of anecdotes and personal style).

