Using statements on either side of a python ternary conditional

拜拜、爱过 提交于 2019-11-29 12:44:31

问题


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?


回答1:


Expressions are evaluated, statements are executed. 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.




回答2:


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




回答3:


First syntax

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.

Second syntax

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!?

Why

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.





回答4:


What version of python you're using? This syntax is not supported in python 2.4 atleast.. the following worked for me on python 3.3 On a sidenote, remove the paranthesis:

import random
x,y=0,0
x += 1 if random.choice([0,1]) else (y +1)


来源:https://stackoverflow.com/questions/14474168/using-statements-on-either-side-of-a-python-ternary-conditional

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!