Why "if-else-break" breaks in python? - python

I am trying to use if-else expression which is supposed to break the loop if the if condition fails, but getting an invalid syntax error.
Sample code:
a = 5
while True:
print(a) if a > 0 else break
a-=1
Of course, if I write in the traditional way (not using the one liner) it works.
What is wrong in using the break command after the else keyword?

If I run this, I get the following error:
... print(a) if a > 0 else break
File "<stdin>", line 2
print(a) if a > 0 else break
^
SyntaxError: invalid syntax
This is because
print(a) if a > 5 else break
is a ternary operator. Ternary operators are no if statements. These work with syntax:
<expr1> if <expr2> else <expr3>
It is equivalent to a "virtual function":
def f():
if <expr2>:
return <expr1>
else:
return <expr3>
So that means the part next to the else should be an expression. break is not an expression, it is a statement. So Python does not expect that. You can not return a break.
In python-2.x, print was not a function either. So this would error with the print statement. In python-2.x print was a keyword.
You can rewrite your code to:
a = 5
while True:
if a > 5:
print(a)
else:
break
a -= 1
You can read more about this in the documentation and PEP-308.

If is an expression, break similar to return is a statement. You can't use two statements in a single sentence (unless you use a semicolon which is ugly). I know it would have been really cool if we can do that, but alas that's the way it is.

To put it in slightly simpler terms, you're misusing the 'one-line if statement' (ternary operator). It always evaluates to an expression (i.e., a value). That is,
<expr1> if <condition> else <expr2>
evaluates to <expr1> if <condition> is True, and to <expr2> if <condition> is False. This resulting value can then be used like any Python value, for example:
y = 0
x = (5 if (y > 0) else 6)
print(x) # 6
Of course, the parentheses are completely unnecessary (even discouraged), but hopefully are useful for understanding the meaning of that line.
Therefore,
print(a) if a > 0 else break
tries to evaluate print(a) (which, by the definition of print() in Python 3, always returns None – perfectly valid, but probably not what you usually want) and then break, which does not evaluate to anything because it is a statement (action), not an expression (value), hence the invalid syntax error.
Hence, if you want to execute one of two statements depending on a condition, you really need the multi-line solution proposed by
Willem Van Onsem. There may be hacky ways to do it in one line, but multiple lines is the usual solution for something like this in Python.

Related

Match True in python

In JavaScript, using the switch statement, I can do the following code:
switch(true){
case 1 === 1:
console.log(1)
break
case 1 > 1:
console.log(2)
break
default:
console.log(3)
break
}
And it's going to return 1, since JavaScript switch is comparing true === (1 === 1)
But the same does not happen when I try it with Python Match statement, like as follows:
match True:
case 1 = 1:
print(1)
case 1 > 1:
print(2)
case _:
print(3)
It returns:
File "<stdin>", line 2
case 1 = 1:
^
SyntaxError: invalid syntax
And another error is returned if I try it this way:
Check1 = 1 == 1
Check2 = 1 > 1
match True:
case Check1:
print(1)
case Check2:
print(2)
case _:
print(3)
It returns:
case Check1:
^^^^^^
SyntaxError: name capture 'Check1' makes remaining patterns unreachable
What would be the cleanest/fastest way to do many different checks without using a lot of if's and elif's?
In JavaScript, using the switch statement, I can do the following code
I definitely wouldn't be using JavaScript as any form of litmus or comparator for python.
If you used 1==1 in your first test case, the below is what both of your test cases are ultimately doing.
match True:
case True:
print(1)
case False: #will never get hit
print(2)
case _: #will never get hit
print(3)
This is why you get the error for the second version. True will only ever be True, so no other case will ever be hit.
Based on your example, it seems like you are trying to use match/case just to determine the "truthiness" of an expression. Put the expression in the match.
match a==1:
case True:
pass
case False:
pass
If you have a lot of expressions, you could do something like the below, although I don't think this is very good.
a = 2
match (a==1, a>1):
case (True, False):
print('equals 1')
case (False, True):
print('greater than 1')
case _:
print(_)
#OR
match ((a>1) << 1) | (a==1):
case 1:
print('equals 1')
case 2:
print('greater than 1')
case _:
print(_)
cases should be possible results of the match, NOT expressions that attempt to emulate the match. You're doing it backwards. The below link should tell you pretty much everything that you need to know about match/case, as-well-as provide you with alternatives.
Match/Case Examples and Alternatives
If you don't want to use the match statement from Python 3.10, you can create a switch-like statement with a one line function and use it with a one-pass for loop. It would look very similar to the javascript syntax:
def switch(v): yield lambda *c:v in c
for case in switch(True):
if case(1 == 1):
print(1)
break
if case( 1 > 1):
print(2)
break
else:
print(3)
Note that if you don't use breaks, the conditions will flow through (which would allow multiple cases to be executed if that's what you need).
Check if you are using Python 3.10, if not then use this instead and
also the match case isn't meant to be used liked this, you're better off using switch case if you're just trying to print something out
The switch case in python is used by creating a function for an 'if-else-if' statement and declaring the case above like:
match = int(input("1-3: "))
def switch(case): #Function
if case == 1:
print(1)
elif case > 1:
print(2)
else:
print(3)
switch(match) #Main driver
else is the 'default' and you can add as many elif statements.

Is there anyway a return statement can contain an if, elif conditionals without the else at the end?

I've been trying to find the answer everywhere but I can't seem to find it. I want to be able to return a statement in my function with an if and (an) elif/s but without an else (just for learning purposes). I am aware that you can do something like this:
def my_func(s):
return "a list" if type(s) == list else "a string" if type(s) == str else "not list or string"
The above shows how python allows you to have an if, as many elifs and an else, but is there any way to be able to have an if and (an) elif/s without the need of an else at the end?
Something to note is that I am speaking about having it on the same line as the return statement, not an expanded one checking on multiple lines.
If it is single line you can have:
def f(x):
if x > 10: return 10
and you do not (explicitly) have a else keyword being used.
Note that this cannot be chained.
Note also that this is anyway equivalent to:
def f(x):
return 10 if x > 10 else None
as any function not returning explicitly otherwise will just return None.
If you consider multi-line constructs, please note that you can do something like:
def f(x):
if x > 10:
return 10
elif x < -10:
return -10
which does not have an explicit else but is equivalent to both:
def f(x):
if x > 10:
return 10
elif x < -10:
return -10
return None
and:
def f(x):
if x > 10:
return 10
elif x < -10:
return -10
else:
return None
So, all in all, there is not much value in not having an else at the end or not using the a if condition else b expression.
It is probably not ideal for readability to chain multiple if-else expressions anyway. Just use multiple lines and enjoy the readability of the code.
If your sole concern is to know if the conditional expression requires else as part of the Python grammar, then the answer is: Yes it does. See the relevant PEP308 for more info.
No it's not possible. See the language specs and the corresponding pep 308
In case of return just add return something if condition else None
As noted, this is a "conditional expression". Expressions always have to resolve to an object but without the else this expression would not. else None is reasonable.
You can write the same idea using return inside the if statement
'' if (type(s) == list): return ....''
In return x, x is an expression which has to be evaluated to some object, no matter what. So you cannot omit the else part of the ternary operator (that's why it's called ternary and not "ternary or binary" ;-) ). That being said, you could shorten your code:
def my_func(s):
return {list: "a list", str: "a string"}.get(type(s), "not list or string")

If and Inline if, what are the advantages and disadvantages?

I'm a little curious about the difference between if and inline if, in Python. Which one is better?
Is there any reason to use inline if, other than the fact that it's shorter?
Also, is there anything wrong with this statement? I'm getting a syntax error: SyntaxError: can't assign to conditional expression
a = a*2 if b == 2 else a = a/w
The advantage of the inline if expression is that it's an expression, which means you can use it inside other expressions—list comprehensions, lambda functions, etc.
The disadvantage of the inline if expression is also that it's an expression, which means you can't use any statements inside of it.
A perfect example of the disadvantage is exactly what's causing your error: a = a/w is a statement, so you can't use it inside an expression. You have to write this:
if b == 2:
a = a*2
else:
a = a/w
Except that in this particular case, you just want to assign something to a in either case, so you can just write this:
a = a*2 if b==2 else a/w
As for the advantage, consider this:
odd_numbers = [number if number%2 else number+1 for number in numbers]
Without the if expression, you'd have to wrap the conditional in a named function—which is a good thing for non-trivial cases, but overly verbose here:
def oddify(number):
if number%2:
return number
else:
return number+1
odd_numbers = [oddify(number) for number in numbers]
Also, note that the following example is not using an if (ternary conditional) expression, but an if (conditional filter) clause:
odd_numbers = [number for number in numbers if number % 2]
The correct way to use the conditional expression is:
result = X if C else Y
what you have is:
result = X if C else result = Y
So, you should remove the result = part from there. The major advantage of conditional expression is that, it's an expression. You can use them wherever you would use a normal expression, as RHS of assignment expression, as method/function arguments, in lambdas, in list comprehension, so on. However, you can't just put any arbitrary statements in them, like say print statements.
Fo e.g. suppose you want all even integers from a list, but for all odd numbers, you want the values as 0. You would use it in list comprehension like this:
result = [x if x % 2 == 0 else 0 for x in li]
Inline if is an expression, so you can not put assignments inside.
Correct syntax would be:
a = a*2 if b == 2 else a/w
As for the usefulness, it's a question of style, and perhaps it would be a good question for Programmers StackExchange.

error when trying to use pass keyword in one line if statement

stumped that this works:
if 5 % 2 == 0:
print "no remainder"
else:
pass
but not this:
print "no remainder" if 5% 2 == 0 else pass
SyntaxError: invalid syntax
The latter is not an if statement, rather an expression (I mean, print is a statement, but the rest is being interpreted as an expression, which fails). Expressions have values. pass doesn't, because it's a statement.
You may be seeing it as two statements (print or pass), but the interpreter sees it differently:
expr = "no remainder" if 5% 2 == 0 else pass
print expr
and the first line is problematic because it mixes an expression and a statement.
A one-line if statement is a different thing:
if 5 % 2 == 0: print "no remainder"
this can be called a one-line if statement.
P.S. Ternary expressions are referred to as "conditional expressions" in the official docs.
A ternary expression uses the syntax you tried to use, but it needs two expressions and a condition (also an expression):
expr1 if cond else expr2
and it takes the value of expr1 if bool(cond) == True and expr2 otherwise.

How does the Python conditional operator workaround work?

From what I have read, I found that a built-in ternary operator does not exist (I will be happy to know more about it.).
I found the following code as a substitute:
def val():
var = float(raw_input("Age:"))
status = ("Working","Retired")[var>65]
print "You should be:",status
I couldn't understand how this code works; can anyone explain me how actually the code is working? I am also interested to know why the ternary operator doesn't exist; any references or links about this will be ore useful.
I'm running Python 2.6.4 on Windows Vista.
Python has a construct that is sort of like the ternary operator in C, et al. It works something like this:
my_var = "Retired" if age > 65 else "Working"
and is equivalent to this C code:
my_var = age > 65 ? "Retired" : "Working";
As for how the code you posted works, let's step through it:
("Working","Retired")
creates a 2-tuple (an immutable list) with the element "Working" at index 0, and "Retired" at index 1.
var>65
returns True if var is greater than 65, False if not. When applied to an index, it is converted into 1 (True) or 0 (False). Thus, this boolean value provides an index into the tuple created on the same line.
Why hasn't Python always had a ternary operator? The simple answer is that Guido van Rossum, the author of Python, didn't like/didn't want it, apparently believing that it was an unnecessary construct that could lead to confusing code (and anyone who's seen massively-nested ternary operators in C can probably agree). But for Python 2.5, he relented and added the grammar seen above.
Python (2.5 and above) does indeed have a syntax for what you are looking for:
x = foo if condition else bar
If condition is True, x will be set to foo, otherwise it will be set to bar.
Examples:
>>> age = 68
>>> x = 'Retired' if age > 65 else 'Working'
>>> x
'Retired'
>>> age = 35
>>> y = 'Retired' if age > 65 else 'Working'
>>> y
'Working'
because True casts to 1 and False casts to 0 so if var = 70
("Working","Retired")[var>65]
becomes
("Working", "Retired")[1]
a nice little shortcut ... but I find it can be a little confusing with anything but a simple condition, so I would go with TM's suggestion
"Retired" if var > 65 else "Working"
indexing into a list
The use of
[expression_when_false, expression_when_true][condition] # or
(expression_when_false, expression_when_true)[condition]
takes advantage of the fact that in Python True equals (but isn't!) 1 and False equals (but isn't!) 0. The expression above constructs a list of two elements, and uses the result of condition to index in the list and return only one expression. The drawback of this method is that both expressions are evaluated.
and-or shortcuts
Since the creation of Python, there was a form of this operation:
condition and expression_when_true or expression_when_false
This takes a shortcut and evaluates only one expression, but has a bug-prone drawback: the expression_when_true must not evaluate to a non-true value, otherwise the result is expression_when_false. and and or are "short-circuiting" in Python, and the following rules apply:
a and b #→ a if a is false, else b
a or b #→ a if a is true, else b
If condition is false, then expression_when_true is never evaluated and the result is expression_when_false. OTOH, if condition is true, then the result is the result of (expression_when_true or expression_when_false); consult the table above.
ternary conditional operator
Of course, since Python 2.5, there is a ternary conditional operator:
expression_when_true if condition else expression_when_false
The strange (if you are accustomed to the C-like ternary conditional operator) order of the operands is attributed to many things; the general intention is that condition should be true most of the time, so that the most common output comes first and is most visible.
Short-circuit boolean expressions
There is also an option to short-circuit logical operations:
>>> (2+2 == 4) and "Yes" or "No"
'Yes'
>>> (2+2 == 5) and "Yes" or "No"
'No'
In your example:
>>> (int(raw_input("Age: ")) > 65) and "Retired" or "Working"
Age: 20
'Working'
>>> (int(raw_input("Age: ")) > 65) and "Retired" or "Working"
Age: 70
'Retired'
Read more about this technique in Charming Python: Functional Programming in Python, Part 1.
in the code that you posted the following line is emulating ternary:
status = ("Working","Retired")[var>65]
here tuple ("Working","Retired") accessed with an index [var>65] which evaluates to either True (1) or False (0). When it's accessed with index 0, status will be 'Working'; if index is 1 then it'll be ``Retired'`. It's a fairly obscure way to do conditional assignment, use the normal ternary syntax that was introduced in py2.5 as was said.
There was originally no ternary operator because "Explicit is better than implicit", and it was seen as unpythonic. I don't like python's ternary op too much, either, but it exists:
x = foo if condition else bar
as shown by TM.
As for status = ("Working","Retired")[var>65],
var > 65 returns a boolean value: either True or False; however, Python treats boolean types quite weakly: True is 1 and False is 0 in some contexts. You can check it out by doing >>> True == 1.
status = ("Working","Retired")[var>65]
This line works as a ternary operator because the expression var>65 returns 1 or 0, depending on whether var is bigger than 65 or not. So if var>65, then the line becomes this:
status = ("Working","Retired")[1]
that is, the second element of the sequence ("Working","Retired"). It looks odd but not if you write it like this instead:
status_sequence = ("Working","Retired")
status = status_sequence[1]
so status = "Retired".
Similarly, if var<=65 then it becomes
status = ("Working","Retired")[0]
and status = "Working".
Only the "status =" line of that code implements something like the ternary operator.
status = ("Working","Retired")[var>65]
This creates a two-element tuple, with strings 'Working' at index 0, and 'Retired' at index 1. Following this, it indexes into that tuple to pick one of the two items, using the results of the expression var > 65.
This expression will return True (equivalent to 1, thus picking 'Retired') if the value of var is greater than 65. Otherwise it will return False (equivalent to 0, thus picking 'Working').
There is a key difference between this approach and the ternary operator, however, although it doesn't matter in your particular example. With the tuple-indexing approach, both values are evaluated but only one is returned. With the ternary operator, only one of the two values is actually evaluated; this is referred to as "short-circuit" behaviour. It can matter in cases like this:
status = funcA() if var > 65 else funcB()
status = (funcB(), funcA())[var > 65]
In the first case, either funcA() is called or funcB() is called, but never both. In the latter case, both are called first, and the results are stored in the tuple -- then only one is picked and the tuple is discarded.
This is especially important to understand if either funcA() or funcB() have "side-effects", meaning they change other data as they execute.
In Python 2.6 and up:
print "You should be {0}.".format("retired" if var>65 else "working")
In Python 3.1 and up:
print ("You should be {}.".format("retired" if var>65 else "working"))
this is the form with the python ternary operator
def val():
var = float(raw_input("Age:"))
status = "Retired" if var > 65 else "Working"
print "You should be:",status
the code you showed is a bit tricky: it creates a two elements tuple whose elements are at position 0 and 1. to select the right element it uses a condition which return a boolean but booleans in python are integers so you can use it as special indexes (they can be either 0 or 1).
trying to give a complete answer based on the answers given here.
the way you found (please don't use this one because it is not very readable):
def val():
var = float(raw_input("Age:"))
status = ("Working","Retired")[var>65]
print "You should be:",status
using the python 2.5+ syntax:
def val():
var = float(raw_input("Age:"))
status = "Working" if var>65 else "Retired"
print "You should be:",status
using the other common method still preferred by some people:
def val():
var = float(raw_input("Age:"))
status = var>65 and "Working" or "Retired"
print "You should be:",status
i personally tend to use the last since the order of the operands is the same as the C ternary operator.
EDIT:
found some problems with the last approach (thx Roberto Bonvallet).
from wikipedia:
this code would break if op1 could be
a "falsy" value (None, False, 0, an
empty sequence or collection, …) as
the expression would return op2
(whether it was truthy or falsy)
instead of the (falsy) op1
so my final suggestion would be to use the 2.5+ ternary operator since it is simple, readable and offers short-circuit behavior.

Categories

Resources