-2

Python one-line if/else statements should function without raising a SyntaxError, but with some keywords they do.

The expected results of pass if arg else pass should be similar to print('') if arg else print(''). While they appear similar, one results in a syntax error and the other does not. Why does the interpreter permit some keywords to be used this way and not others?

def foo(arg):
    #raise BaseExecption if arg else raise BaseException
    #pass if arg else pass
    print('') if arg else print('')
    
foo(True)

Note: The commented out lines of code will generate a SyntaxError.

Lucian
  • 95
  • 7
  • 2
    There is no one-line if/else statement in Python. If you want a statement, use a standard multi-line if/else. `x if y else z` is a conditional expression. – user2357112 Jan 04 '23 at 23:59
  • @user2357112 [There's](https://stackoverflow.com/questions/70249144/python-if-statement-to-a-single-line) [multiple](https://blog.arrowhitech.com/python-if-else-in-one-line-the-simple-guide/) [examples](https://stackoverflow.com/questions/11529273/how-to-condense-if-else-into-one-line-in-python). Also called a ternary expression. – Lucian Jan 05 '23 at 00:01
  • 2
    Some rando's blog is not an authoritative source. The fact that some blog calls it a statement doesn't mean it's a statement. As for your other links, none of your other examples call it a statement. – user2357112 Jan 05 '23 at 00:05
  • 2
    If you check the [official documentation](https://docs.python.org/3/reference/expressions.html#conditional-expressions), you will see that `x if y else z` is an expression. It selects one of two sub-expressions to evaluate, not one of two statements to execute. – user2357112 Jan 05 '23 at 00:05
  • @user2357112 You don't need an authoritative source for common conventions. Your style recommendations do not imply that the interpreter is incapable of handling a hack of a "ternary" operator. That's an artificial constriction. – Lucian Jan 05 '23 at 00:08
  • 1
    You are seriously misunderstanding what's going on if you think any of what I've said is a matter of style. – user2357112 Jan 05 '23 at 00:11
  • It's not a style recommendation or an artificial constriction (except in the sense that, of course, Python itself and its syntax was created by people). *Statements* and *expressions* can certainly seem pretty similar when you're starting out, I know, but they're functionally not the same. – CrazyChucky Jan 05 '23 at 00:14
  • @CrazyChucky I understand that once the arg bound by if - else is evaluated, then the other arguments will be evaluated. I know that print('') will return None, which is a return. Which works. Why does pass, raise, or any other keyword not behave similarly. – Lucian Jan 05 '23 at 00:23
  • `raise`, `pass`, and `return` are *statements*. Each has its own rules for the specific context in which you can use it; you can't do `x = raise`, for instance. Function calls and anything else that evaluates to a value (even if that value is `None`) are *expressions*. The one-line `if ... else` is an expression, and can only contain other expressions. And putting `print('')` (or just `None`) isn't, in and of itself, a `return`... it's just that *any* time a function gets to the end of its code without hitting a `return`, it automatically returns `None`. – CrazyChucky Jan 05 '23 at 00:33
  • @CrazyChucky Great! Then `lambda x: x + 1 if arg else lambda x: x + 1` would also be a valid expression. (Also meaning, you cannot return pass or raise pass) – Lucian Jan 05 '23 at 00:39
  • ...Yes, but probably not what you intend. That's all one lambda function. And correct, you cannot `return pass` or `raise pass`. I've no idea what that would even mean. – CrazyChucky Jan 05 '23 at 00:41
  • @CrazyChucky This creates a really dumb solution. If we just create a function called `pass_me` that does has one line of `pass` and then `pass_me() if arg else pass_me()` would work fine. Which is disturbing. Instead of using pass, those lambda functions are functionally similar. – Lucian Jan 05 '23 at 00:47
  • 1
    `pass` inside a function has no effect on anything outside the function. Literally all `pass` means is "This function (or other block, like an `if` or `try`) is empty." It's there as a placeholder because an entirely *blank* block has no indented text and thus raises a syntax error. I honestly have no clue what problem you're trying to solve here. – CrazyChucky Jan 05 '23 at 00:50
  • I was trying to understand what was happening. I did not understand that print secretly had a return behind it, which would be the difference where raise, pass, and return do not. I was attempting something strange, and wasn't expecting this SyntaxError. Now I know that those are expressions to be evaluated, and print happens to be returning something for evaluation. This is not something you read in everyday Python, but does change your overall understanding. – Lucian Jan 05 '23 at 01:00

1 Answers1

4

pass is a statement (specifically a "simple statement"); it isn't an expression. That's a problem because conditional expressions ("ternaries") only work on expressions. Here's their entry in the Python grammar:

disjunction 'if' disjunction 'else' expression

All parts there are expressions besides the keywords.

This isn't really a problem through since you're abusing conditional expressions in the first place. Instead, use a full if-statement.

Carcigenicate
  • 43,494
  • 9
  • 68
  • 117
  • This would imply that print('') is both a disjunction and an expression in the above example? – Lucian Jan 05 '23 at 00:11
  • @Lucian: "disjunction" is an expression category in the grammar. It's the next precedence level up from conditional expressions. (The grammar is structured like this to ensure expressions are parsed correctly according to precedence.) – user2357112 Jan 05 '23 at 00:14
  • 2
    @Lucian In the grammar I believe function calls fall under the category of `primary`, which is, through a bit of a convoluted path, an expression. Expressions include disjunctions. – Carcigenicate Jan 05 '23 at 00:22
  • I assumed it was taking the form: ? : that's been maneuvered into if else Pass when evaluated, I would expect to just continue on. I'd expect the one line version to behave similar to the full if statement. if arg: pass else: pass – Lucian Jan 05 '23 at 00:32
  • 1
    @Lucian "Pass when evaluated,": `pass` isn't evaluated. It doesn't survive compilation (it isn't even compiled into anything). It's simply a syntactic placeholder in the source for when a statement requires a body, but you don't want/need to supply one. The purpose of a conditional expression is to have the conditional expression evaluate to one of two expressions. From that point of view, it doesn't make sense to include something in a conditional expression that doesn't evaluate to value. – Carcigenicate Jan 05 '23 at 00:51
  • @Carcigenicate I was not aware of the requirement 'evaluate to a value' was present or that print was evaluating to a value. I would assume that if print returns a value, then so would other keywords. But, this is not a valid assumption. Clearly. – Lucian Jan 05 '23 at 00:59
  • 1
    `print` is a plain function in Python 3. It evaluates to a value line any other function. And the precence of keywords is not a good indicator. Some keywords can be part of expressions (`await`), and some can't. – Carcigenicate Jan 05 '23 at 01:09
  • 1
    @Lucian Just to to make part of Carcigenicate's last comment explicit, `print` is not a keyword. (Neither are other built-in functions like `max` or `sum`, or indeed any functions.) – CrazyChucky Jan 05 '23 at 03:54