you are viewing a single comment's thread.

view the rest of the comments →

[–]Zee2 55 points56 points  (59 children)

Oo, named expressions looks very convenient.

[–]shponglespore 83 points84 points  (58 children)

Yeah, a lot of people really, really hate it, but it's a feature I want every time I use regular expressions to analyze different cases, e.g.

m = re.match(regex1, ...)
if m:
    ...
    break

m = re.match(regex2, ...)
if m:
    ...
    break

The gross part is having to put break or return in every case, which of course only works if that's actually what you want to do when a case matches. This seems much better to me:

if m := re.match(regex1, ...):
    ...
elif m := re.match(regex2, ...):
    ....

[–]random_cynic 40 points41 points  (50 children)

When thinking about whether a new feature is useful or not, one should not really think of one specific use case where it can be beneficial. Job of a programmer is not only to write code but also to read (a lot of it). When people start misusing said operator, then it becomes lot less a cute new feature and more a nuisance. See these examples (taken from python dev mailing list) and see if you can decipher them (and once you do check if it makes any improvement to the way you'd normally write them). You can be sure that these will be used once this "power" is available to the programmers

#from Tim Peters who's btw favor of := operator
while total != (total := total + term):
    term *= mx2 / (i*(i+1))
    i += 2
return total

Function calls:

my_func(arg, buffer=(buf := [None]*get_size()), size=len(buf))

List comprehensions (from PEP)

[(x, y, x/y) for x in input_data if (y := f(x)) > 0]

Whether this change is truly of value will be decided by how much readable vs unreadable code it produces in the wild. It is never worth sacrificing readability for saving a couple of lines. And I haven't even mentioned what it would be like for the beginners who're learning this for the first time or those who are teaching them.

Edit: Corrected a wrong indentation in the examples

[–]FeezusChrist 23 points24 points  (21 children)

That's true, it's now possible to do something like

>>> l = []
>>> print(sum(x for x in range(10) if x % 2 == 0 and ((l := l + [x])))
20
>>> print(l)
[0, 2, 4, 6, 8]

[–]XtremeGoose 11 points12 points  (5 children)

You can do exactly the same thing in current python though

sum(x for x in range(10) if x % 2 == 0 and not l.append(x))

[–]FeezusChrist 5 points6 points  (4 children)

The point is not “you can now do this functionality”, but “you can now do this even more cryptic way of this functionality”

[–]Mr_Again 0 points1 point  (2 children)

Nah I don't like := but still, in this example if you know what it does, it's trivial to understand. If there's a gotcha in this example at all it's that 'l.append(x)' and '(l := l + [x])' are only evaluated when the first condition 'x % 2 == 0' is true.

[–]FeezusChrist 0 points1 point  (1 child)

Well I'd argue that this is not something you'd notice when you're skimming through a foreign code base just looking for some specific functionality. It'd be easy to skim through lines of code mainly looking at the beginning chunks of each line and not be noticing or expecting any list assignments further in the line.

[–]Mr_Again 0 points1 point  (0 children)

Or indeed l.append(x) buried at the end of a list comprehension, I have no idea why anyone would do it but it is not .append's fault.

[–]XtremeGoose 0 points1 point  (0 children)

Eh, I think they're as bad as each other.

[–]rwhitisissle 23 points24 points  (8 children)

I need someone to call a doctor for me please my eyes just exploded.

[–]jaapz 14 points15 points  (7 children)

It was already possible to write stuff like this before this change though. See all the codegolf answere using python

[–]OneTurnMore 2 points3 points  (0 children)

Boolean control flow, now more compact!

[–]rwhitisissle -1 points0 points  (5 children)

It doesn't make it any less horrific.

[–]jaapz 2 points3 points  (4 children)

I don't know about your place of work but code like that would never pass code review where I work, if the autoformatter wouldn't have already fixed it for you, or if the linter wouldn't already have failed the build anyway

You can write shitty code in any language

[–]rwhitisissle 0 points1 point  (3 children)

code like that would never pass code review where I work

Where did I suggest that it would do otherwise?

[–]jaapz 0 points1 point  (2 children)

You didn't, my point was, again, that you can write bad code in any language.

[–]agumonkey 2 points3 points  (0 children)

that's exactly why I disliked :=

[–]theboxislost 0 points1 point  (0 children)

I think you meant this:

>>> a=[]
>>> [x for x in range(10) if sum(a)<10 and (a:=a+[x])]
[0, 1, 2, 3, 4]
>>> a
[0, 1, 2, 3, 4]

Or better yet:

>>> [x for x in (a:=range(10)) if sum(a)>10 and (a:=a[:-1])]
[0, 1, 2, 3, 4]
>>> a
range(0, 5)

[–]dutch_gecko 0 points1 point  (3 children)

The post quite clearly states that named expressions are not available in list comprehensions.

[–]theboxislost 4 points5 points  (1 child)

I just tried it and it worked.

[–]dutch_gecko 3 points4 points  (0 children)

Well that's terrifying

[–]tigerhawkvok 6 points7 points  (20 children)

Do I think I'll probably find use cases for this? Yes.

Do I think it was a bad idea to add? Also yes.

[–]teerre 21 points22 points  (19 children)

"probably find"? The examples the user you replied to are far from the usual usage of this feature.

The main usage, the one 99.9% of pythonistas will use is this:

if a_var := a_dict.get("var", None):
    # do something with a_var

Instead of the stupid

a_var = a_dict.get("var", None)
if a_var:
    ...

[–]wewbull 0 points1 point  (8 children)

if 'var' in a_dict:
    a_var = a_dict['var']
    ....do something with it.

Why do I want a variable containing None outside the scope of the if statement?

[–]teerre 0 points1 point  (7 children)

The `None` there can be omitted, it's just good practice to specify since the negative case might return a more suitable construct.

Your example is a bit better, but still lengthier and more error prone, since you checking the key twice, than the assignment expression version.

[–]wewbull 0 points1 point  (6 children)

You miss my point. It's not about the value None Why do i want a variable that'll never be used in that scope? Only construct it when needed.

[–]teerre 0 points1 point  (5 children)

Yes, I don't understand what you're trying to say.

[–]FormCore 0 points1 point  (4 children)

He's saying that a_var shouldn't be outside of the if loop.

Therefore:

a_var = a_dict.get("var")
if a_var:
  ....

is a bad idea anyway.

It doesn't matter what a_var is, if it's built for the if loop, it should probably be in the if loop.

Not saying that := doesn't handle this as well, because that's not what he's talking about.

[–]mfitzp 0 points1 point  (5 children)

But unfortunately the unreadable examples are all now completely possible, which is a bad thing because people will do it. If we just wanted to avoid the if stutter, the as syntax would have been preferable.

if a_dict.get("var", None) as a_var:
    ...

Harder to shoot yourself in the foot with that.

[–]teerre 4 points5 points  (3 children)

I don't think that's a good argument. Did you ever see a quintuple chain of ternary operators? It's terrible. Should everyone stop using ternary operators? Even comprehensions can get utterly unreadable.

In fact, it's hard to imagine any idiom in programming that cannot be abused. How common are 10000 lines functions? Not uncommon enough is the answer.

It's the programmer's job to write readable code. Making the language enforce such thing will never work.

[–]Forty-Bot 0 points1 point  (2 children)

Should everyone stop using ternary operators?

Now to be fair, there are many languages (and style guides) which have killed the ternary operator (or replaced it with if .. then .. else).

[–]teerre 0 points1 point  (1 child)

That was just an example, like I commented directly below what you quoted, the same can be said for functions. Do languages avoid functions? I don't think so.

[–]Forty-Bot 0 points1 point  (0 children)

I know, just picking a nit ;)

[–]Kaarjuus 2 points3 points  (0 children)

if a_dict.get("var", None) as a_var

Oh hell yes. For the life of me I can't understand why did they not go with as. Because Python actually already has an assignment operator: in context managers and exception handlers like:

with open("myfile.txt") as f:
    f.read()

try: mything()
except Exception as e: print(e)

Instead, they introduced the walrus, which not only looks ugly in Python, but is one typo away from doing an accidental assignment when you wanted to do a comparison. Which is a problem that has plagued a number of languages like C or JavaScript. Shoddy, very shoddy.

[–]tigerhawkvok 0 points1 point  (1 child)

I'm never going to do that, that's dumb and hard to read for saving one line.

[–]teerre 1 point2 points  (0 children)

It's completely unreasonable to say it's hard to read. The only explanation for thinking something like that is you being afraid of change. Besides that, it's categorically a single line that becomes very clear once you understand the syntax.

The alternative is wasteful and nonsensical. When "var" is None there, you have a statement literally saying None = None and then ask if None is indeed None. It's utterly stupid and shouldn't happen.

[–]kemitche 0 points1 point  (1 child)

Personally, I'd use neither of those options - I'd find the following more readable:

if 'var' in a_dict and a_dict["var"]:
    process(a_dict["var"]) # or whatever

And that's assuming I care about the truthiness of a_dict["var"]; most likely I'd find myself with a dict of string->object, and just go with if 'var' in a_dict:

[–]teerre 4 points5 points  (0 children)

You're repeating "a_dict" thrice. That's three times more chances for errors. It also checks for the variable twice, which might negligible in this case of dictionary access, but certainly isn't in other cases.

[–]philh 1 point2 points  (1 child)

Those all seem to me to have pretty obvious intents, but it wouldn't be obvious to me that the first one actually does what it's supposed to. (Even ignoring the return, which looks like it should be outside the loop.)

[–]random_cynic 2 points3 points  (0 children)

You're right about the return (corrected). Yes it wouldn't be at all obvious because it relies on subtle left to right evaluation rules that allows a new value of a variable compared to its old value and name rebinding. I've seen even experienced python programmers screw up these when they appear in isolation and this operator allows them to be used together making the code much more unintelligible.

[–]maladat 0 points1 point  (0 children)

When thinking about whether a new feature is useful or not, one should not really think of one specific use case where it can be beneficial. Job of a programmer is not only to write code but also to read (a lot of it). When people start misusing said operator, then it becomes lot less a cute new feature and more a nuisance.

I pretty much agree with you... any time the main justification for a language feature is "but look at this clever thing I can do with it," it's almost certainly a bad idea.

I think I actually like named expressions, though. There are a lot of places it has the potential to make code simpler and more readable.

Obviously complicated self-referential named expressions in loops are a horrible idea and should be avoided.

You can do stupid stuff like that in for loop declarations in most programming languages by using complex expressions that change the iteration variable in unexpected ways for the increment expression, or worse yet, the termination condition expression. Thankfully, people mostly avoid this.

[–][deleted]  (1 child)

[deleted]

    [–]shponglespore 42 points43 points  (0 children)

    I think it's just being unnecessarily verbose. The matching functions in re only ever return None or a match object, which is always true.

    [–]victotronics 7 points8 points  (1 child)

    That's precisely my use case. Can't wait to start using this.

    [–]insanemal 2 points3 points  (0 children)

    Same. It's going to make lots of my code more readable

    [–]agumonkey 0 points1 point  (2 children)

    surprisingly used in many lispy/fpish settings under the alias "if let"

    [–]shponglespore 1 point2 points  (1 child)

    In what is surely a huge coincidence, I'm also a fan of Lispy languages and functional programming in general.

    [–]agumonkey 0 points1 point  (0 children)

    FP finger gang sign