Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

But now there are two ways to do assignment. That's not very pythonic, is it?


You think that's bad? Check out:

    a = 17
    print("a=", a)
    print("a=" + str(a))
    print("a=%s" % a)
    print("a={}".format(a))
    print(f"a={a}") 
    # python 3.8 =>
    print(f"{a=}")
So many ways to do it...

But, if it sounds like I agree with you, I actually don't. I feel that the Zen of Python has taken on an almost religious level of veneration in people's minds, and leads to all sorts of unproductive debates. One person can latch onto "there should be one obvious way to do it" and another onto "practicality beats purity" and another onto "readability counts." Who's right? All can be. Or none. All could be applied to this particular case.

The Zen of Python is just a set of rough heuristics, and no heuristic or principle in the field of software development applies 100% of the time, IMHO. <= except for this one ;)


> there should be one obvious way to do it

In cases like this, different ways to do it (all equally good) are needed to get a good coverage of different tastes in obviousness and different nuances in the task.

The point is not uniformity, but avoiding the unpleasant and convoluted workarounds caused by non-obviousness (thus making the language easy to use).

String formatting is not trivial: there is the split (sometimes architectural, sometimes of taste, sometimes of emphasis) between concatenating string pieces, applying a format to objects, setting variable parts in templates, and other points of view; and there is a variety of different needs (cheap and convenient printing of data, dealing with special cases, complex templates...)


And there was also:

    print string.Template("a=$a").substitute(a=a)


I never felt like there was only one way to do something in Python. Every Stack Overflow question has a multitude of answers ranging from imperative to functional style and with various benefits and drawbacks.

Python is one of the least "only one way to do things" languages I've used. This even extends to its packaging system, where you can choose between virtualenv, pipenv, pyenv, etc. Same goes for the installation method too, do you want to install Python with Anaconda or use the default method?

As for my personal take on this feature: I think it's really useful. When web-scraping in Python, I oftentimes had to do this:

  while True:
      html_element = scrape_element()
      if html_element:
          break
      sleep(10)
Now I can do this:

  while not html_element := scrape_element():
      sleep(10)


Prior to that you could use the special two-argument version of the `iter` function which makes it act completely different than the single argument version:

    for html_element in iter(scrape_element, None):
this calls scrape_element() until it returns None, returning each value.


It used to be more or less true in the early days. For me the "one obvious way to do it" ship has sailed with list comprehensions which was introduced in 2.0 (released in 2000)


Packaging isn’t really anything to do with the language syntax, or the zen of Python. Any critiques on Python-the-language?

And pyenv is just a version manager, like rbenv or nvm. I wouldn’t consider its existence confusing, not would I say being able to install something in more than 1 way has any relevance to the zen of Python!

Should Python create some cross-platform Uber-installer so that there is only one download link?


I don't see why the "zen of Python" shouldn't be applied to its tools too. Tools are part of the developer experience and few/none of the statements/guidelines in the zen of Python are exclusive to Python the programming language.

Regardless of what pyenv, the rest of my comment about the complexity of Python's tooling still stands. There's too many choices. I also seen people use pyenv as an alternative to virtualenvs, which is something I have never seen with nvm.

I don't understand why the Python community hasn't coalesced around a single solution to package management that has minimal complexity. It seems like pipenv is the solution, but there is controversy around it and it should have come several years ago. The fact that Python packages are installed globally by default is also pretty terrible, I much prefer it when applications bundle their dependencies. When I do `npm install --global`, the resulting program will always work, regardless of what other packages I have installed on my system.

> Any critiques on Python-the-language?

The point of my original comment was not to necessarily critique the Python programming language, rather it was to point out that adhering to the "zen of Python" is a lost cause because the language/development environment is not designed as a curated experience.

And my original comment did make points about Python-the-language. I talked about how there's many ways to do a single task in Python. One of the responses to it even proved my point:

"Prior to that you could use the special two-argument version of the `iter` function which makes it act completely different than the single argument version: <code sample>".

That unfortunately demonstrates my point.


>Every Stack Overflow question has a multitude of answers ranging from imperative to functional style and with various benefits and drawbacks.

This is one of the reasons I love Python. It's a great exercise to rewrite the same code imperative, recursive, with generators, with iterables, etc. Python is very good at supporting a wide range of programming styles.


I see this criticism every time the walrus operator is brought up.

You do know that this:

    x := 1
Is going to be a syntax error, right? The walrus operator is not permitted in the case of a simple assignment statement. It's only in an expression.


But it used to be that any expression on its own was a valid statement. Is that going to change?

When is an expression allowed to have := in it, is

  (x := 1)
on its own allowed?


For contexts where the walrus is not allowed, see [0]. You'll find that it's generally possible to circumvent the restriction by parenthesising the expression. So yes,

    (x := 1)
is a valid (but poorly written) statement.

But while there are now two ways of doing assignment, I wonder how often people will actually encounter situations where it's difficult to figure out which choice is better.

[0] https://www.python.org/dev/peps/pep-0572/#exceptional-cases


Allowed, yes. But the PEP that introduced walrus operators says not to do it.

Every possible line of code has an alternate ugly way to write it. This isn't a valid criticism. Anyone who decides to start writing simple assignment statements like that deserves to be ridiculed for writing ugly code.


Of course, and there's no reason to write such code.

I just dislike that the simple syntax rule "any expression can be used as a statement" now has an exception.

I haven't been able to think of scenarios where that might have consequences (code generation or refactoring tools?) but that doesn't say much as I'm not that smart.

Edit: having looked at the cases that are disallowed, they remind me of generator expressions. Those are usually written with parens, that can optionally be omitted in some cases. := is the same except they can be omitted in so many cases that it's easier to list the cases where they can't.

I think a generator expression used as a statement already requires the parens, even though they can be omitted e.g. as a single parameter of a function call. So that's probably ok then.


Not really, but neither are ugly nested if statements (Flat is better than nested, readability counts, etcetera). You need to make tradeoffs.

Maybe it would have been better to only have a single := assignment operator to begin with. But it's a few decades too late for that.

For what it's worth, := is for expressions only. Using it as a statement is a syntax error. So there won't be a lot of cases where both are equally elegant options.


Regular = can only be used in statements. Walrus := can only be used in expressions. There's no overlap there. However, := does simplify certain expressions (like those nested if-else statements and the common "while chunk := read()" loop), which I think does justify its existence.


This honestly makes it seem more confusing to me. The fact that there is now an operator that can only be used in certain statements just makes things more confusing. And if there really is no overlap, then why wasn't the "=" operator just extended to also work in expressions? "while chunk = read()" seems like it makes just as much sense without adding the confusion of another operator.


One of the good things about not using the "=" operator is that you cannot accidentally turn a comparison into an assignment, a feature that is a common cause of errors in other languages that do support it. By adding a completely different character to the operator it is not very likely to cause bugs, compared to just forgetting to type that second =


Is it really that common? I made this typo a few times in my life. It was corrected every time before the program actually run because the compiler warned me about it. I don't see how you can make this mistake if you're not aggressively trying (by turning off warnings for example).


I guess it is not common, but by using the = operator you would not get the warning, and instead get unexpected behaviour.


I expect the PEP authors want to avoid the "while chunk == read()" class of bugs

ninjaedit: indeed https://www.python.org/dev/peps/pep-0572/#why-not-just-turn-...


And also the converse (but no less dangerous) "if value = true"


> The fact that there is now an operator that can only be used in certain statements just makes things more confusing

The new operator (like many Python operators) can only be used in expressions (statements can contain expressions, but expressions are not a subset of statements.)

> The fact that there is now an operator that can only be used in certain statements just makes things more confusing

Because the “=” operator is the thing that defines an assignment statement. Even if this could be resolved unambiguously for language parsers, so that “statement defining” and “within expression” uses didn't clash, it would be create potential readability difficulties for human reading. Keeping them separate makes the meaning of complicated assignment statements and assignment-including expressions more immediately visually clear.


>it would be create potential readability difficulties for human reading

I think the major argument (at least, the one I see most frequently) is that the walrus operator does create readability difficulties for humans, which is exactly why many people view it as non-pythonic. This is one of the few times I've seen someone argue that ":=" makes things more readable.


An argument against expression assignment is that it can create readability problems compared to separating the assignment from the expression in which the value is used. Even most supporters of the feature agree that this can be true in many cases and that it should be used judiciously.

This is in no way contrary to the argument that the walrus operator improves readability of expression assignments compared to using the same operator that defines assignment statements.


There are at least three ways to iterate over a list and create a new list as a result. That's not very pythonic, is it?


The Zen of Python states:

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

There are plenty of ways to do assignments. Walrus assignments are only the obvious way in certain cases, and in general there aren't other obvious ways. For testing and assigning the result of re.match, for instance, walrus assignments are clearly better than a temporary.

I can think of lots of nonobvious ways to do assignments, like setattr(__module__...)


I can think of more than two ways to do a lot of things in Python. Besides the ":=" doesn't work exactly the same.

Also, I can't bring myself to call it the walrus operator. Sorry, guys. I had a Pascal teacher ages ago who pronounced it "gets" and that has always stuck.


Assignment can be confusing already.

  >>> locals()['a'] = 1
  >>> a
  1
If anything, the walrus operator allows for tightly-scoped assignment, which is good in my opinion.


You don't even need the locals() function to get into trouble:

    x = [1, 2, 3, 4]
    def foo():
        x[0] += 3  # Okay
    def bar():
        x += [3]   # UnboundLocalError
    def qux():
        x = [5, 6, 7, 8]  # Binds a new `x`.


    def bar():
        x += [3]   # UnboundLocalError
This is an especially funky one. x.extend([3]) would be allowed. Presumably x += [3] is not because it expands to x = x + [3]... However, the += operator on lists works the same as extend(), i.e. it changes the list in-place.


dis.dis(bar) shows:

              0 LOAD_FAST                0 (x)
              2 LOAD_CONST               1 (3)
              4 INPLACE_ADD
              6 STORE_FAST               0 (x)
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE
So INPLACE_ADD and STORE_FAST are essentially doing x = x.__iadd__([3])


This isn't really true. There's one way to do assignment, `=`, and one way to do special assignment that also works as an expression, `:=`. You should always use `=` unless you *need `:=`.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: