Why is it prohibited to use statements on either side of python's ternary conditional? I can't see any obvious reason how a few of the following naive syntax examples could be ambiguous or broken - but I'm sure there must be a good reason why it's disallowed!
>>> x, y = 0, 0
>>> (x += 1) if random.choice([0, 1]) else (y += 1)
^
SyntaxError: invalid syntax
>>> (x if random.choice([0, 1]) else y) += 1
SyntaxError: can't assign to conditional expression
>>> print 'hello world' if random.choice([0, 1]) else raise StandardError()
File "<stdin>", line 1
print 'hello world' if random.choice([0, 1]) else raise StandardError()
^
SyntaxError: invalid syntax
Can you give an example where allowing a statement to be used in a ternary could be dangerous or ill-defined?
Expressions are eval
uated, statements are exec
uted. This is something completely different.
Python developers have decided to strictly separate between expressions and statements . This will make you "fail early" instead of ending up with an error that is hard to debug.
In other languages, like C(++), you can do things like
if (i=15) {...}
but you could also do
if (i==15) {...}
In the first example, you assign 15 to the variable i
, and this new value of i
is then evaluated by the if
.
Even worse are increment/decrement operators. What is the difference between
if (i--) {...}
and
if (--i) {...}
?
For the first example, the value of i
is evaluated, then it is incremented. For the second example, the order is the other way around, so if i
was 1
before, the result is different.
In Python, such things are prohibited by the syntax, due to the experience gathered with such concepts in other languages.
I'm not quite sure if this completely answers your question, but I hope I at least could give some insight into the pythonic approach.
This has nothing to do with conditional expressions1. Python programs are made up of statements. Most parts of most statements are expressions. Expressions only contain other expressions.
y += 1
is a statement, and isn't allowed where an expression is expected. The ternary conditional as a whole is an expression, and each of the 3 parts of it are expressions. There's no more reason to allow (x += 1) if random.choice([0, 1]) else (y += 1)
than there is to allow any of the following monstrosities:
x = (y += 1)
def foo(x=(x += 1)):
print x
print [x += 1, x *= 10]
Expressions are things that can be evaluated to some value; statements are things that don't have a value. If you allow statements as the "when true" or "when false" operands of a conditional expression, then why not allow any statement in any expression? After all, it would complicate the grammar to special case it so that the conditional expression was the only kind of expression that could contain a statement.
x = y + pass
[return True, import sys]
None of these make any sense. Neither does (x += 1) if random.choice([0, 1]) else (y += 1)
, because the whole point of conditional expressions is to be expressions. So it would more realistically appear in a statement, such as:
z = (x += 1) if random.choice([0, 1]) else (y += 1)
You could conceivable rule that the "value" of x += 1
is the value of x
(either before or after 1 has been added), as C does. But it makes the language considerably more complicated. And that still doesn't solve the problem of:
z = pass if (import sys) else (while False: print 17)
What is the value of pass
? Of import sys
? Of a while loop?
To make this work you would have to separate out "statements" as a class of things that exists in the grammar of Python into "expressiony statements" and "normal statements", or invent some arbitrary rules about what the value of certain kinds of statement are. Probably both.
The simple fact is, if you're trying to write this as a single statement:
(x += 1) if random.choice([0, 1]) else (y += 1)
Then Python already has the syntax for expressing this idea, and it is this:
if random.choice([0, 1]):
x += 1
else:
y += 1
There's no need to introduce the complexities to the language (and to readability) of putting statements as components of expressions just so you can obfuscate if
statements by writing them as conditional expressions (whose value is ignored).
1 Call it a "ternary conditional" if you must, but "ternary" or "ternary operator" is just plain silly. That it has 3 operands is hardly the most important thing about it; that's like calling +
"binary operator".
Your first syntax is really a traditional if
statement, which you tried to write in one line with condition and first action reversed. It's overkill to use one-line constructs this way, and it's ugly and unreadable. Python is not C.
Your second syntax doesn't work because Python's conditional expression result is an rvalue
, not lvalue
(the link talks about C and Microsoft, but it's just a common term, occurring in every language). rvalue
is an expression, which may occur on the right side of the assignment only. lvalue
, however, may occur either on the right or on the left. When lvalue
is on the right, it's value just used in evaluation of whatever on the left side. When lvalue
is on the left, it's address is used to store value of right-hand side expression after evaluation.
In Python, everything is reference, and lvalue
allows the change of what the reference points to, but rvalue
doesn't.
Example:
a = 5 # here a is lvalue
b = a + 1 # here a is rvalue and b is lvalue
Another example of rvalue
-only expressions are constants. From the point of view of the interpreter, your second attempt looks just like
42 = 42 + 1 # WHAT THE HECK!?
As for why was it decided to make conditional expression free of statements (whoops, looks like we've already found the reason!): as PEP 308 states, the conditional expression was initially introduced for use instead of error-prone x and y or z
boolean expression, which worked the same because of short-circuit evaluation.
If Python was to allow statements like yours in conditionals, then it had to allow, for example, raise
. It is considered a bad Python code style to stuff a lot of logic in one-liner (as I've said above).
Look at this
result = some_long_name / some_other_name if some_other_name != 0 else raise ZeroDivisionError
It looks like an expression at the beginning, but then it behaves like a statement and even raises exceptions! :( Bearing in mind the Python's recommendation to strip lines at 79 characters, you wouldn't not even see the raise
in, say, multi-window side-by-side view.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With