Why does Python assignment not return a value?

PythonPython 3.xLanguage Design

Python Problem Overview


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.

Python Solutions


Solution 1 - Python

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.

Solution 2 - Python

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).

Solution 3 - Python

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.

Solution 4 - Python

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.

Solution 5 - Python

  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.]

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmaxView Question on Stackoverflow
Solution 1 - PythonLaurence GonsalvesView Answer on Stackoverflow
Solution 2 - PythonbenjiminView Answer on Stackoverflow
Solution 3 - PythonGlenn MaynardView Answer on Stackoverflow
Solution 4 - PythonPeter RowellView Answer on Stackoverflow
Solution 5 - PythonFRDView Answer on Stackoverflow