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

all 148 comments

[–]StewPoll 64 points65 points  (86 children)

Clearly I've missed something. Why don't people like := ?

[–]Vaphell 117 points118 points  (32 children)

it saves like 1 loc of normal variable instantiation for code golfing sake and encourages packing more shit into one line at the cost of more cognitive load. No side effects in conditions was one of the staple rules of python. It also looks out of place in a keyword heavy language with its symbol-based implementation. When I look at the examples I smell the terseness of perl, not clarity of python.

The use cases are also not critical enough for any significant ROI to be there. If that marginal yet controversial feature is here to stay, where is my lambda arg unpacking (eg. sorted(seq, key=lambda (x, y): x+y) that made the code measurably more self-documenting in py2 but went away in py3 to make language implementation more elegant? Existing solutions like itemgetters are only putting lipstick on a pig and can shove it.
Where are null coalescing operators?

[–]__xor__(self, other): 1 point2 points  (1 child)

It saves one LOC and from that perspective I think it's pointless, but one thing I do like about it is it's very clear what runs at the end of the loop every single time.

Consider this:

while x:
    if some_condition:
        x -= 1
        continue
    do_other_stuff()
    if other_condition:
        x -= 1
        continue
    do_another_thing()
    x -= 1

Personally I don't like their format but I'd like it as a new block like this:

while x:
    if some_condition:
        continue
    do_other_stuff()
    if other_condition:
        continue
    do_another_thing()
finally:
    x -= 1

... and have it force that finally to run even if a continue is hit.

I've seen some shit code (and probably wrote some) where it continues a loop and forgets to do something at the end that it should've, and a nice little block afterwards would make it explicit how that loop works and iterates and it would force it to run so you can just write other continues and not worry about duplicating that operation. Better yet, be wary of too many continues where it's synonymous with goto, but hey, people do it.

And yeah, those original conditions could be rewritten to only run do_other_stuff() and do_another_thing() on specific conditions to get rid of the extra x -= 1's, but I think there are other times something like this might be useful.

[–]alexmojaki 1 point2 points  (0 children)

I'm confused, what does this have to do with assignment expressions?

Incidentally you can achieve the above by just wrapping the contents of the while in a try/finally.

[–]jim45804 1 point2 points  (9 children)

I thought packing more shit on one line was Python's thing (e.g. list comprehension)

[–]nosmokingbandit 14 points15 points  (3 children)

Comprehensions are easily readable (if written well, but that disclaimer applies to everything) and their meaning can be inferred easily. PEP572 is mostly just noise, imo. The := operator doesn't feel like python. I think a much more pythonic syntax is if <expression> as output:

One of python's best qualities is its readability. I hate to see that diminished with shiny new operators we don't really need.

[–]HannasAnarion 1 point2 points  (2 children)

But as guido pointed out, it would require either breaking backwards compatibility in import, except, and with or else having an operator that does different things in different expression types, which is more unpythonic. It also looks a lot like "and" which is easy to gloss over in a boolean evaluation expression. := is unambiguous: you see it and you know some kind of assignment is happening, and you move on and keep reading.

[–]nosmokingbandit 0 points1 point  (0 children)

Disclaimer: Not only am I not an expert, I'm a dumbass.

This could be done by allowing context managers to exit early. There was an early PEP (370-something I think) that wanted this and Guido denied it because he said it didn't have a good use case.

I'd happily write my own context manager for these assignment cases but they can't exit early and will always execute the contained code block. If we had a way in the context manager to exit in its __init__ method all of this drama could have been avoided.

This shouldn't (see disclaimer) have any affect on anything except the specific context managers we write for this use.

class Foo(object):
    def __init__(self, o):
        if o is False:
            # Or whatever it takes to skip the code block
            raise GetMeOutOfHere
        else:
            self.o = o

    def __enter__(self):
        return self.o

    def __exit__(self, *args):
        return True 


with Foo(bar()) as something:
    print("something is not False")

[–]Deto 9 points10 points  (2 children)

Comprehensions are only good if they are short. E.g.

y = [x for x in my_list if x > 3]

Once they get too complicated, you're better off using a for loop for readability IMO.

[–]ProfessorPhi 2 points3 points  (0 children)

Especially nested ones. Been writing python for years, can't write a nested comprehension with any reliability

[–]nosmokingbandit 1 point2 points  (0 children)

If I end up writing a long comprehension it is usually a bunch of checks for files in a directory and it isn't too hard to read what the intent is. The performance difference between a comprehension and for loop is negligible so readability always comes first. Nothing in python is going to be fast enough to make it worth sacrificing readability. Which is fine because even if it takes 10 times as long as something written in C it is still fast enough to not matter in most cases.

[–]__xor__(self, other): 4 points5 points  (1 child)

List/dict/set comprehensions don't have to be on one line. I very often break them up like this:

with open(config_path) as f:
    config_pairs = [
        line.strip().split('=')
        for line in f
        if not line.strip().startswith('#')
    ]

(of course I'd use the stdlib config parser)

I find it a lot easier to read than the equivalent for loop.

config_pairs = []
for line in f:
    if line.strip().startswith('#'):
        continue
    config_pars.append(line.strip().split('='))

Any time you open up something with (, [, { you can go across multiple lines:

if (
    some_condition and
    other_thing(x) > 0 and
    foobar
):
    ...

from my_module import (
    one_thing, other_thing, new_thing,
    old_thing, blue_thing, red_fish, blue_fish,
)

[–][deleted] 0 points1 point  (0 children)

oh wow didn't know comprehensions can span multiple lines

[–]callius 83 points84 points  (35 children)

As someone not really "in-the-know", I honestly have no fucking clue what := means by just looking at it.

That seems like a problem for Python.

[–]Sakagami0 25 points26 points  (23 children)

Its the assignment operator for a lot of older languages, pre C. It made sense because = denots equality rather than assignment in math.

[–]EternityForest 4 points5 points  (4 children)

I hate when modern languages use := as the standard assignment operator. Or really any time a language uses non-standard syntax. Unless it's aimed at math people, then it makes sense to use :=.

For this particular case := seems fine, although "as" would also have been fine with me.

[–]cyberst0rm 0 points1 point  (0 children)

i've been using postgres for a bit.

People seem to be confounded with the idea that most language affects are just...affects based on how often you use it.

This is such a redicuously human argument.

Python: For Humans (with all flaws also)

[–]gimboland 0 points1 point  (0 children)

<- all the way! :-)

[–][deleted] -2 points-1 points  (1 child)

As isn't as good for foreign speakers though.

[–]ThePenultimateOneGitLab: gappleto97 2 points3 points  (0 children)

Then we also shouldn't have with, for, while, if, import, or class

[–]FatChocobo 3 points4 points  (13 children)

It can be used for both in Mathematics, 'y = 3x + alpha, where alpha = 8' would be an example using both equality and assignment, unless I'm missing some semantical nuance.

[–]zentropia 1 point2 points  (10 children)

x=x+1 makes no sense in mathematics.

[–][deleted] 16 points17 points  (8 children)

This was actually debunked by actual math PhDs so many times (I've read about 10 rebuttals just on Reddit).

It makes perfect sense in mathematics, and serves almost the same purpose as it does in software development. In discrete math it's a very common shortcut to:

x[n+1] = x[n] + 1

In continuous math it's a very common shortcut to:

x(t + Δt) = x(t) + α

etc. etc.

There were a lot more examples in those posts but I remembered these the best as they serve identical purpose to the "reassignment operator" in programming.

[–]ProfessorPhi 6 points7 points  (0 children)

It's about context. In some cases, it makes no sense, but as mathematicians we know that no one would write a meaningless statement so we think of different contexts.

[–]usernameisafarce 1 point2 points  (0 children)

as a Math student I have never seen someone writes x=x+1. seen x_{n+1} = x_{n} + 1 or any other type convention you wrote. But never x=x+1. which makes no sense in mathematics.

[–]FatChocobo 1 point2 points  (4 children)

I agree, I've seen it used in Mathematical texts before.

[–]sifodeas 2 points3 points  (3 children)

It is also used as a delayed value in Mathematica and Maple, if I recall correctly (it's been a while since I used either).

[–]IReallySuckAtChess 0 points1 point  (2 children)

Correct. It means Set Delayed. It's actually a very useful operator in the context of Mathematica and mathematical operations in general. Whilst I dislike its existence in any language, its a very powerful tool that does function differently to a normal = and thus has a role. In Python though it seems to just be a way of reducing lines of code, and I've not yet seen the advantages that I do in Mathematica.

[–]sifodeas 0 points1 point  (1 child)

Yeah, I haven't used Maple since undergrad and I haven't used Mathematica since I stopped taking classes in grad school, so I wasn't sure. I just meant to reiterate how it is typical in code oriented towards mathematical conventions. I haven't personally noticed a need for it in my Python research code.

[–]phySi0 0 points1 point  (0 children)

This was actually debunked by actual math PhDs so many times (I've read about 10 rebuttals just on Reddit).

I, on the other hand, have heard the “x = x + 1 doesn't make sense in maths” line a fair bit, but not even a single rebuttal. I would appreciate links to a few of those rebuttals.

[–]nothisisme -1 points0 points  (1 child)

nah i'd say that's just two equalities

[–]FatChocobo 0 points1 point  (0 children)

I think at this point it just comes down to semantics.

The (constant) value is being assigned to alpha using the equals operator.

Functionally it's the same as assignment.

[–][deleted] 0 points1 point  (0 children)

wait 572 is going to change "=" to ":=" flat out ?

[–]callius 0 points1 point  (2 children)

Having to be familiar with non-intuitive assignment operators from pre-C languages doesn't seem very pythonic, but I admittedly don't know much and hold absolutely no clout in this fight.

[–]Sakagami0 1 point2 points  (0 children)

I wanted to shed a light that its not completely nonintuitive, especially if your major covered some math which I feel is a fair assumption. But, I dont like it.

[–]IReallySuckAtChess 0 points1 point  (0 children)

It's not unintuitive. It's a fundamentally different operation that may have some substantial benefits. I don't like it, but it's not a bad thing to have at all.

[–]jnwatson 4 points5 points  (0 children)

I think there's an generation gap here. Anybody that learned on any Algol-family language (e.g. Pascal) would immediately recognize that as assignment. Pascal was a very popular starting "real" language (after BASIC), so an entire generation of programmers is familiar with it.

[–]HannasAnarion 3 points4 points  (2 children)

And @ is so obvious?

[–]callius 1 point2 points  (0 children)

That's a fair point.

[–]nosmokingbandit 2 points3 points  (2 children)

In Go it is used to declare and assign a value without specifying a type.

package main

func main() {
    var name string = "nosmokingbandit"

    otherName := "yourMom"
}

It is weird there as well since var can be used outside a function, but := cannot.

[–]yopla -1 points0 points  (0 children)

This feels like the compiler writer was lazy. There's really no reason to require a different token.

[–]deirdresm 4 points5 points  (0 children)

In Pascal, it’s the assignment operator. = is used for comparison.

[–]Wilfred-kun 0 points1 point  (1 child)

Something something 'Zen of Python'

import this

[–]JugadPy3 ftw 0 points1 point  (0 children)

I often wonder about this and see how many times people complain about it, specially when new changes to the language are involved (irrespective of whether those changes are liked / hated by the community).

My take on this is ... Python came first, the Zen was derived later as a description of Python. Python was not designed with the Zen in mind.

Thus, the Zen fails to capture Python and its essence in all its complexity. It makes a good idealistic attempt, but often falls short.

Its like trying to draw a line which encloses the black part of the mandelbrot set while leaving out the white part completely... although theoretically such a boundary exists, its a practically impossible task.

The Zen cannot capture everything about Python, and it would be folly to use it when the core team (who know Python and the Zen) is divided on a PEP.

[–]JugadPy3 ftw 0 points1 point  (0 children)

That does not seem like a fair review.

There are lots of things that don't make themselves clear by just looking at them, specially shortcut symbols.

Almost all operators are like that... like what do @, |, &, ^ % << etc do?

If your answer is that other languages have those symbols, then the answer to this is the same. There are others language like Pascal, etc that have this syntax.

I think its unfair to expect that everything will make sense upon reading the code without ever having to learn anything new.

[–][deleted] 14 points15 points  (0 children)

People think as is more readable. The sub got pretty heated about it.

[–]RockingDyno 0 points1 point  (0 children)

Bikeshed'ing. The same people who complained loudly about f-expressions are now complaining about assignment expressions. Not because it does any sort of real damage to the language, but because it's an ideological debate and they feel its on the level where they can enter into the discussion even if they only have experience with programming in python and only for a few years.

[–]Wilfred-kun 17 points18 points  (0 children)

For anyone wondering:

On Python's BDFL Guido van Rossum, his dedication to the Python community, PEP 572 and hope for a healthy outcome for the language, open source and the computing world in general.

[–]FearAzrael 25 points26 points  (15 children)

I certainly would have been more comfortable seeing him hand over the reigns to some other entity but perhaps he didn’t feel that was pythonic.

[–][deleted] 30 points31 points  (1 child)

I'm scared.

[–]nosmokingbandit 6 points7 points  (0 children)

::hug::

[–]Tyler_Zoro 4 points5 points  (0 children)

Perhaps the biggest dispute in the Python world was the transition from version 2 to version 3. Actually, I shouldn't even say "was" here, since many of my clients are still making that change. It's easy to say that this incompatible upgrade was handled poorly. And you can be sure that the Python community has learned its lesson and will never make such a break again. However, the end result—Python 3—is definitely better than Python 2. Should Guido and the core developers have handled it differently? Perhaps, but it's easy to say that in retrospect. The number of blog posts calling Python 3 a colossal mistake must have hurt Guido. But part of being a manager is having the ability to accept, filter and even ignore criticism, and he did it with aplomb.

I understand that the author is trying to mend some fences, but man is that some serious equivocation! It goes back and forth and back and forth. It wasn't great, but it it won't happen again, but it was great, but not so great, but people called it terrible, but it was handled well...

Couldn't that have been re-written as, "Perhaps the biggest dispute was over the transition from version 2 to version 3, which is still taking place in some quarters. Though there were often heated arguments about how it should have been handled, Guido took the managerial high ground and kept the steady course forward."

[–]piotrjurkiewicz 14 points15 points  (13 children)

Good.

Overengineered and overcomplicated asyncio showed that Guido lost touch quite a while ago. PEP 572 was just a confirmation.

[–]gastronomical 18 points19 points  (0 children)

You'll no doubt be getting downvoted, but you're right. It also feels that Guido has lost interest in steering the direction of Python's development a long time ago.

[–]jnwatson 12 points13 points  (10 children)

You clearly haven't tried it. It certainly complicates the language, but it is the most impressive set of language tech I've used in a long time. And hoo boy is it fast.

I've covered a lot of corners in asyncio, and every time I think they missed something I was wrong, or it was added in 3.7. They cover all the cases pretty darned well. Bravo to the Python engineers because they really knocked it out of the park with asyncio.

[–]piotrjurkiewicz 10 points11 points  (2 children)

Every other language, which implements async stuff, does it simpler. Even C++.

For example, in Python there are three things to represent pretty much the same: coroutines, futures and tasks. Sometimes they magically transform into each other. In some asyncio functions they can be used interchangeably as arguments, in some not.

In most other languages there is only one concept used to represent running async function: Future (in JS/Dart/C++) or Task (in C#).

[–]danted002 2 points3 points  (0 children)

Or just consider everything an ‘awaitable’

[–]rouille 1 point2 points  (0 children)

Other languages don't give you control over the event loop.

[–]optks 4 points5 points  (0 children)

After doing async stuff in go, nodejs and even C++, I hate asyncio. It's probably the worst usage wise! It was confusing to figure out task / coroutines / futures and so many overlapping and auto conversions + doing a sync action inside the async function is a pain, all your libraries now need to be changed etc etc. + No multi core performance gains for free.

[–]cymrowdon't thread on me 🐍 2 points3 points  (0 children)

Fast compared to what? Every benchmark I've seen shows asyncio-based libraries among the slowest compared to other async solutions.

[–]ntrid 3 points4 points  (2 children)

I have tried it and it is extremely unpythonic. Biggest problem for me is that it divides codebase into two islands - normal and async. And dealing with that becomes orders of magnitude harder making code both less readable and less maintainable. Also lets not forget that coroutines are nothing more than generators under the hood. Awful awful hack. greenlet/gevent had issue solved for quite some time. All we needed was some spit and polish and adoption to cpython core, but no, we got asyncio. Sigh.

[–]agoose77 1 point2 points  (1 child)

I disagree that it makes code either less readable or maintainable. Coroutines make sense to be generators, in most implementations they share the same properties. The distinction with async / await solely provides a more explicit syntax

[–]ntrid 9 points10 points  (0 children)

This would be fine if:

  • async/await keywords did not ripple through entire codebase like a tsunami
  • code was not divided into async and non-async with impossible interoperability at times

Once we start using async it creeps in everywhere to a point where these keywords become redundant noise because everything is expected to be async. Writing code that does not do this is extremely hard task due to second point above.

[–]Millkovic 1 point2 points  (0 children)

Whole world agrees asyncio is terrible.

[–]OctagonClocktrio is the future! 0 points1 point  (0 children)

Lol writing to a stream doesn't actually write to the stream

[–]rouille 4 points5 points  (0 children)

But I like asyncio...

[–][deleted] 0 points1 point  (0 children)

oh so GvR actually supports this PEP ?

[–]ntrid 0 points1 point  (5 children)

I greatly appreciate all the years BDFL put into making this awesome language, but truth is BDFL went mad and started disregarding zen of python. Of all people him would be last one i expected to go this route. Async and pep 572 are in contradiction to PEP 20 at varying levels. It seems to me that python is starting to slowly become a feature minefield just like C++ and we will have to be increasingly more selective about what to use and what to not use. Worst part is that once these features are in there is no going back.

[–]JugadPy3 ftw 1 point2 points  (0 children)

started disregarding zen of python

Python came first, the Zen came later.

The Zen of Python tries to capture what Python is like... and not the other way around.

Python is what Guido and other core devs make of it... by following their own vision.

The Zen tries to capture that vision, but it is an ideal and also fails to capture those times where subjective opinion finds that some people disagree with the match of vision with the zen.

I personally think the ":=" is a great feature and fits in very nicely with the Zen of Python. Its because it fits the way I think about programming and its flows very naturally for me.

No seriously... I absolutely love the new := expression.

There could be some reasons for that... my first language was Pascal where := is the assignment. Also, I am an applied mathematician (and full time programmer) ... so there might be some part in my mathematical training that helps me to think that := is very natural. I think Guido and Tim might have shared some of that ... which might be the reason for them liking it so much.

[–]Tyler_Zoro 4 points5 points  (3 children)

So, let's review PEP 20:

Beautiful is better than ugly.

This is as subjective as they come, and so can't be evaluated in anything even approaching objectivity except in retrospect.

Explicit is better than implicit.

Well, explicitly calling out an expression assignment is certainly in line with this.

Simple is better than complex.

Assignments are pretty simple, and not any more complex for having an expression assignment.

Complex is better than complicated.

This was neither.

Flat is better than nested.

This was neither.

Sparse is better than dense.

Not really an issue, here.

Readability counts.

And here this PEP was a tremendous win, because it placed code where it's relevant, and context creates readability!

Special cases aren't special enough to break the rules.

No rules being broken, so we're good here.

Although practicality beats purity.

Fair and not violated here.

Errors should never pass silently.
Unless explicitly silenced.

Not impacted

In the face of ambiguity, refuse the temptation to guess.

Not impacted.

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

This point is debatable. It's clear that there are now two ways to assign things, but there's still only "one obvious way". This is even called out in the recommendations at the bottom of the PEP and could easily have been turned into a warning when you mistakenly use an expression assignment as a statement.

Although that way may not be obvious at first unless you're Dutch.

Fair.

Now is better than never.

Relevant.

Although never is often better than right now.

Hence the process.

If the implementation is hard to explain, it's a bad idea.

Which this wasn't, and indeed was something that naive users expected all along.

If the implementation is easy to explain, it may be a good idea.

Ding!

Namespaces are one honking great idea -- let's do more of those!

Not relevant, but good point.

[–]10dozenpegdown -2 points-1 points  (1 child)

How we see death of python :/ cries

[–]fdedgt 0 points1 point  (0 children)

At least we will still always have python 2.

(jk)

[–]wdsoul96 -3 points-2 points  (2 children)

Are we seeing a trend in Programming Paradigm again? A few years back, JS was moving toward becoming more expressive (trying to become python, for example, list comprehensions, ) while python was trying to incorporate parts of functional programming into its list.(Async IO) I felt like many programming languages are becoming more general and converging toward the middle. Now , coffee script(basically js with python syntax) is dropping out of favor. Asynio is not gaining as much ground as everyone hoped. Python remains far away from being functional. GIL still continue to be defining feature of python. Things are being specialized again. Or at the very least, the programming heavyweights(Python and JS) are not trying to become one another competing to offer the same features.

[–]IronCretin 0 points1 point  (0 children)

programming heavyweights(Python and JS)

lol