72

Why is Python assignment a statement rather than an expression? If it was an expression which returns the value of the right hand side in the assignment, it would have allowed for much less verbose code in some cases. Are there any issues I can't see?

For example:

# lst is some sequence
# X is come class
x = X()
lst.append(x)

could have been rewritten as:

lst.append(x = X())

Well, to be precise, the above won't work because x would be treated as a keyword argument. But another pair of parens (or another symbol for keyword arguments) would have resolved that.

max
  • 49,282
  • 56
  • 208
  • 355
  • What does your code example have to do with the question? – Ignacio Vazquez-Abrams Feb 02 '11 at 01:16
  • @Ignacio: my mistake. The version edited (@Laurence Gonsalves) is good. – max Feb 02 '11 at 02:09
  • 2
    "Are there any issues I can't see?" It appears that "much less verbose code" is somehow not a problem. Terse, cryptic code golf seems like an issue. Are you discounting that one for some reason? – S.Lott Feb 02 '11 at 02:17
  • @S Lott: I guess I was thinking of simple examples (like the one I gave, which I don't see as too cryptic); I agree that the ability to abuse this might be one of the reasons against this feature. – max Feb 03 '11 at 17:14
  • I like this question and other questions like it. I believe the answers and comments are very constructive and helpful. I'm just amazed the closure police hasn't shut it down already as NC ;-) – cfi Feb 19 '13 at 09:26
  • @cfi I asked this a while ago, before I learned that SO community frowns on such questions. I think I just got lucky with this one; similar questions I asked later were closed :) – max Feb 19 '13 at 19:18
  • The one (and I think more obvious) case of assignment-is-not-expression that annoys me to no end every time: row = cursor.fetchone() \ while row: \ ... \ row = cursor.fetchone(), instead of while row = cursor.fetchone(): May be add it to the question? – cyberj0g Jan 14 '16 at 10:36
  • `lst.append(X())` also works perfectly fine – blues Aug 02 '18 at 18:42
  • @blues I wanted to have `X()` assigned to variable `x`; *and also* have that very same `X()` appended to the list. – max Aug 03 '18 at 05:56

5 Answers5

52

There are many who feel that having assignments be expressions, especially in languages like Python where any value is allowable in a condition (not just values of some boolean type), is error-prone. Presumably Guido is/was among those who feel that way. The classic error is:

if x = y: # oops! meant to say ==

The situation is also a bit more complicated in Python than it is in a language like C, since in Python the first assignment to a variable is also its declaration. For example:

def f():
    print x

def g():
    x = h()
    print x

In these two functions the "print x" lines do different things: one refers to the global variable x, and the other refers to the local variable x. The x in g is local because of the assignment. This could be even more confusing (than it already is) if it was possible to bury the assignment inside some larger expression/statement.

Laurence Gonsalves
  • 137,896
  • 35
  • 246
  • 299
  • +1. This strikes me as a real "reason", as opposed to the old business about "if x=y". – harpo Feb 02 '11 at 01:30
  • 17
    The "= vs ==" thing is a red herring. It hasn't been an actual problem in C/C++ for years, ever since compilers learned how to warn about it. I've been using both languages for over ten years and I can't remember the last time I was bitten by that. – Glenn Maynard Feb 02 '11 at 04:11
  • 19
    @Glenn: Python doesn't have a compiler to warn you, there is no concept of "almost syntax errors" like that. – Lennart Regebro Feb 02 '11 at 09:20
  • 2
    @Lennart: Of course Python has a compiler; it's used every time you load new code. Python also has a warning mechanism, which the compiler uses. (I don't think it even really matters. Even when I'm stuck in MSVC which doesn't warn about it, I don't have any trouble. It seems like the sort of problem people have exactly once as a beginner, then go through their career thinking it's a huge problem. It's legal to strangle people who use Yoda conditions, right?) – Glenn Maynard Feb 02 '11 at 11:21
  • 6
    @Glenn: But since its used *at runtime* it can't warn about these things in the same sense. Compiler warnings in C are sort of "almost syntax errors", it says "Are you really sure you want this". That concept has no Python equivalent. It's either correct, or it's a syntax error. And Python tries to avoid supporting things that lead newbies to make errors. :-) – Lennart Regebro Feb 02 '11 at 12:04
  • @Lennart: Sure it does--try `assert (0, "foo")` with warnings enabled (python -Wd). – Glenn Maynard Feb 02 '11 at 21:10
  • 1
    @Glenn: Newbies typically don't run with -Wd, so that wouldn't help. You can't have a warning against newbie mistakes in -Wd. Maybe = vs == is a red herring in C, but it wouldn't be in Python. (That said, the other reasons may still be better). – Lennart Regebro Feb 02 '11 at 21:52
  • 11
    @Lennart: Trying to prevent beginners from making beginner mistakes is futile and not--at least not by itself, and at the expense of something else--a good design rationale; and "we can't do that with a warning because our warning system isn't discoverable enough" points at a problem with the warning system or the documentation--you don't design a language around the warning system, you do the reverse. – Glenn Maynard Feb 02 '11 at 22:23
  • 1
    @Glenn: But to make it more discoverable, we must have the warnings on by default. Which means they happen by runtime and not compile time, which is the difference I tried to point out compared with C. – Lennart Regebro Feb 02 '11 at 22:40
  • @Lennart: All developers should know how to turn on warnings, just as you do in any language; that's a documentation problem at most. That should be fixed directly (mention it more clearly in tutorials, just as C compiler warning flags like -Wall are well-known), not by designing the language under the assumption that nobody will ever see warnings. (That's not to say that I think this particular thing should be allowed, of course.) – Glenn Maynard Feb 03 '11 at 00:27
  • 1
    @Glenn: Sure, but this is still different things we are talking about with compile time and runtime warnings. And runtime warning warn about things when they happen (if they do), compile time when compiled, so the C compiler warnings are more something like pyflakes or pylint. So if there was an official pysyntax checker you would run, then, IMO, you would have a good argument. :) – Lennart Regebro Feb 03 '11 at 08:17
  • 1
    @Lennart: It sounds like you're saying that Python warnings only happen when the line in question is executed, and that's not the case. Python compiler warnings happen at compile-time, even if the code is never executed. Python's compile-time happens to be *during* runtime rather than a separate step, but that doesn't matter--unless code is released without the developer ever running the program, anyway. – Glenn Maynard Feb 03 '11 at 08:50
  • 8
    The point again being that there is no "compile time" in normal usage of the word, as compiling is done on demand during running. Which is the difference I have been pointing out all the time here. It's getting boring repeating myself, so I'm going to stop now. – Lennart Regebro Feb 03 '11 at 09:18
  • Just to clarify for my own education: Python *will* generate some warnings for the code that it never executes, as long as the interpreter sees the file at all? That sounds as good as compile-time warnings for all practical purposes to me, assuming none of the `import` statements are placed inside `if` conditions :) – max Feb 03 '11 at 19:33
  • 2
    @max: And that assumption is wrong. `import`s are done dynamically, and this is considered a feature. See Python code for the `os` library and how it implements `os.path` by importing different modules depending on the system architecture. I think Lennart's point finally became clear in his last comment. – cfi Feb 19 '13 at 09:21
  • @cfi Well, this can be easily addressed by executing `python -m py_compile xyz.py` for each `xyz.py` in the project (there is even a built-in utility `compileall` to assist with that). But then I read @Lennart Regebro's comment: _So if there was an official pysyntax checker you would run, then, IMO, you would have a good argument._ So I conclude that the built-in compiler isn't powerful enough to catch the unintended usage of `=` instead of `==`. – max Feb 19 '13 at 19:43
  • @max: Why would you compile modules not required for running the script? Again, look and think about the os.path example. On a windows machine you wouldn't want to compile a module written to run only on unix, and vice versa. – cfi Feb 20 '13 at 09:52
  • @cfi - I'm just referring to the argument that Python is ill-equipped to handle `==` vs `=` typos, since it doesn't have a compile-time syntax check. I think if one really wanted to do that, it might work - as long as one added a more powerful syntax checker (of course, also assuming developers would actually use that to check for warnings before shipping their products). – max Feb 20 '13 at 11:29
  • 1
    "Presumably Guido is/was among those who feel that way." He does - see the last paragraph of https://docs.python.org/3/tutorial/datastructures.html#more-on-conditions; Guido is the author of this official tutorial as far as I know. On another note, even though assignment is not an expression, chained assignments like `a=b=4` is allowed and functions as expected. – flow2k Jan 14 '19 at 10:01
  • In principle, when cpython checks for unclosed braces and unexpected indents (when generating the bytecode), it could also emit nonfatal messages about poor style (such as `if a=b:`) or the included editor used by beginners could default to always run `lint` first. Not sure how desirable this would really be. – benjimin Oct 15 '20 at 03:00
41

Assignment (sub-)expressions (x := y) are supported since Python 3.8 (released Oct. 2019), so you can indeed now rewrite your example as lst.append(x := X()).

The proposal, PEP 572, was formally accepted by Guido in July 2018. There had also been earlier proposals for assignment expressions, such as the withdrawn PEP 379.

Recall that until version 3, print was also a statement rather than an expression.

The statement x = y = z to assign the same value to multiple targets (or rather, multiple target-lists, since unpacking is also permitted) was already supported (e.g. since version 1) but is implemented as a special syntax rather than by chaining successive assignment sub-expressions. Indeed, the order in which the individual assignments are performed is reversed: nested walruses (x := (y := z)) must assign to y before x, whereas x = y = z assigns to x before y (which may be pertinent if you set/assign to the subscripts or attributes of a class that has been overloaded to create some side-effect).

benjimin
  • 4,043
  • 29
  • 48
15

The real-world answer: it's not needed.

Most of the cases you see this in C are because of the fact that error handling is done manually:

if((fd = open("file", O_RDONLY)) == -1)
{
    // error handling
}

Similarly for the way many loops are written:

while(i++ < 10)
    ;

These common cases are done differently in Python. Error handling typically uses exception handling; loops typically use iterators.

The arguments against it aren't necessarily earth-shattering, but they're weighed against the fact that it simply isn't that important in Python.

Glenn Maynard
  • 55,829
  • 10
  • 121
  • 131
  • 3
    Sometimes you may need things like `while (x[k] += 1) <= limit: ...`; you could rewrite it without the assignment as an expression, but that requires a non-trivial restructuring. – musiphil Dec 31 '14 at 00:44
7

I believe this was deliberate on Guido's part in order to prevent certain classic errors. E.g.

if x = 3: print x

when you actually meant to say

if x == 3: ...

I do agree there are times I wished it would work, but I also miss { and } around a block of code, and that sure isn't going to change.

Donald Duck
  • 8,409
  • 22
  • 75
  • 99
Peter Rowell
  • 17,605
  • 2
  • 49
  • 65
4
  1. Python's syntax is much less verbose than C's syntax.
  2. It has much more sophisticated scope rules than C.
  3. To use parentheses in every single expression reduces the code readability and python avoids that.

If assigments were expressions, these and many other features would have to be re-worked. For me it is like a deal you have to make in order to have such readable code and useful features. In order to have

if a and (h not in b): ...

rather than

if (a && !(h in b)) { ... }

[not talking about the classic (if a = b:) kind of error.]

Rafe Kettler
  • 75,757
  • 21
  • 156
  • 151
FRD
  • 2,254
  • 3
  • 19
  • 24