One liner with nested if/else conditions - python

Real python noob here, and I'm a bit puzzled by looking at the result of this nested if/else one-liner:
>>> num_arr = [5, 10, 15]
>>> [i**2 if i == 10 else i-5 if i < 7 else i+5 for i in num_arr]
[0, 100, 20]
Shouldn't the result be [0, 15, 20] since 10 is greater than 7?

As I mentioned in the comments, the "correct" version (note the parentheses) is:
[(i**2 if i == 10 else i-5) if i < 7 else i+5 for i in num_arr]
Your "problem" stems from the chaining of your if statements, which execute in the English reading order (left to right). So it reads like this:
[i**2 if i == 10 else (i-5 if i < 7 else i+5) for i in num_arr]
where i=5, i != 10 in the "first expression", so you move to else; where i < 7 so you get i-5, or 0
where i=10, i == 10 so you get i**2, or 100
where i=15, i != 10 so you move to else, where i is not less than 7, so you get i+5, or 20
From the documentation:
To make the semantics very clear, a list comprehension is equivalent to the following Python code:
for expr1 in sequence1:
for expr2 in sequence2:
...
for exprN in sequenceN:
if (condition):
# Append the value of
# the expression to the
# resulting list.
That means yours looks like:
[[expr1] if [condition] else [[expr2] if [condition] else [expr3]] for ...]

Here's the expanded version of the one liner:
for i in num_arr:
if i == 10:
i ** 2
else:
if i < 7:
i - 5
else:
i + 5
The one-liner if statement is basically: [action] if [condition] else [action]. In other words, do this if said condition is met. Otherwise, do this action.
It's a reason I don't like one-liners is because they become less Pythonic. The code above is more verbose but it's easier to understand.

The value if the condition is True is stated before the if and the value if False is after the else (see https://docs.python.org/2.5/whatsnew/pep-308.html).
Result is i-5 for 5 because i is not 10 but it is < 7
Result is i ** 2 for 10 because i == 10
Result is i + 5 for 15 because i is not 10 and it is not < 7
Hence [0, 100, 20]

Related

If statement where if 3 of 5 conditions are true in Python

I was wondering how to have an if statement in python that will react to any 3 out of 5 conditions being true to make the if statement true.
if (a>2) and (b>3) and (c>2) and (d>6) and (e>4):
list.append(True)
This code would add true to the array "list" if all 5 conditions are met but I was wondering how to get it to work if any 3 of the 5 are true to then append true to "list"?
How about this?
values = [a>2, b>3, c>2, d>6, e>4]
if sum(values) >= 3:
list.append(True)
Just copying what #Blckknght said in the comments b/c I can't explain it better. This works because:
True is equal to 1 and False is equal to 0 (the bool class is a subclass of int)
sum sums numbers, so you check for a sum of booleans >= 3!
Similar to #Tom 's answer but with using tuples that leads to (maybe) faster construction, garbage collection and (maybe) a nicer syntax.
if sum((a > 2, b > 3, c > 2, d > 6, e > 4)) >= 3:
lst.append(True)

While loop and logical operators in Python - how does it work?

Why is it that when I type the code:
A = 0
B = 0
while A<10 or B<5:
A+=1
B+=1
print(A)
print(B)
the loop doesn't stop when B=5; instead, it continues to add 1 to both A and B until A = 10, then it stops. The same thing happens when I swap the order, like:
while B<5 or A<10:
When I replaced 'or' with 'and', the code stops when B=5, just the way I wanted. I'm just confused as to how 'or' and 'and' actually works.
It's similar to English:
or gives True when any of the conditions on both sides are True.
and gives True when all of the conditions on both sides are True.
A < 10 and B < 5 means that your loop while continue while A is less than 10 AND B is less than 5 (checking both conditions) or in other words, it will only stop if when A has a value more than or equal to 10 OR B has a value that is more than or equal to 5 (any one of the two).
As for A < 10 or B < 5, the loop will continue if A has a value that is less than 10 OR B has a value that is less than 5 (any one of the two). That is, if A = 10 and B = 2 the loop will still continue because B still fulfills the condition. The loop will only terminate if A has a value that is more than or equal to 10 AND B has a value that is more than or equal to 5 (the two has to be true).

Best method to get variable that does not equal a count function parameter?

I am using the count function to check if exactly 2 out of 3 variables are equal to a number. After the function is called, is there an easier or better way to find the variable that did not equal the number in the count function than what I did below? I am looking for a pure Python solution without packages.
This is what I am currently using to get the variable that does not equal the count function parameter:
num1 = 5
num2 = 7
num3 = 5
list = [num1, num2, num3]
if list.count(5) == 2:
for e in list:
if e != 5:
print(e)
I am using Python 3.7
I'm not sure if this counts as "easier", but it's certainly shorter to do this:
list = [5,7,5]
[x for x in list if x != 5]
I would suggest using numpy's vectorized operations, which are fast and very intuitive.
import numpy as np
a = np.array([5,7,10])
Then
>>> a == 5
array([ True, False, False])
>>> (a == 5).sum() # how many are equal to 5?
1
>>> a[a!=5]
array([ 7, 10])
Of course, please let know if you need a pure python approach.
You want to print the variable whose count is not 2
lst = [5, 7 , 5]
for i in lst:
if lst.count(i) != 2:
print(i)
# 7
Or
print(*(i for i in lst if lst.count(i) != 2))
def single(tri):
return tri[2] if tri[0] == tri[1] else tri[:2][tri[0] == tri[2]]
It uses the coercion False = 0 and True = 1.
In contrast to the list expressions, it tests only exactly 2 times for equality (the list expressions test 3 times). So this might be the fastest solution available. Test:
single([5, 5, 7]) ## 7
single([5, 7, 5]) ## 7
single([7, 5, 5]) ## 7

Assign Variable(True/False) to many conditions

I am doing Grok Learning Course, and have come across a problem. Sorry if I have done anything badly; not a professional at coding:
I am trying to make a variable that will be True/False depending on many conditions:
So,
x = True if y > 2 , g < 4 , n != 3
I have tried different ways to do this like:
Assigning variable to each of my conditions:
if y > 2:
ytrue = True
etc..
However, I want to find a better way of doing it than that, which is long and tedious.
Thankyou.
It's as simple as
x = y > 2 and g < 4 and n != 3
which evaluates to True if all three conditions hold and False otherwise.
As you are new to programming, here's how you read it.
First you evaluate the right hand side which is
y > 2 and g < 4 and n != 3
This is evaluated left to right. First it evaluates y > 2 which will either be True or False. If False the whole right hand side is False and False will be assigned to x. Otherwise it keeps on evaluating all of the other sub-expressions on the right hand side until one of them is False or until you've found every one to be True, in which case the whole right hand side is True.
In either case, True or False, your variable x gets the desired value.
x = y > 2 and g < 4 and n != 3
If you have multiple expressions and if you want to do expression on a expression using and's & or's combinations its better to put it on like this
x= ((y > 2 and g < 4) or n != 3)
Comparison operators like (>, <, !=, etc.) all end up being evaluated down to True or False by the python interpreter.
Python also has Boolean the operators and, or, not (Read more in Section 4.2 of the python docs) but you can use these in combination with the comparison operators to chain them. So your assignment becomes:
x = y > 2 and g < 4 and n != 3
You can use ternary conditional operator :
['false','true'][y > 2 and g < 4 and n != 3]
How it works ?
True == 1, False == 0
because booleans are a subclass of int , so [y > 2 and g < 4 and n != 3] produce a integer value but ['false','true'] takes it as index value.
True gives ['false','true'][1]
And False gives ['false','true'][0]
Test case:
y=4
g=1
n=3
print(['false','true'][y > 2 and g < 4 and n != 3])
output:
false
Test case2:
y=4
g=1
n=1
print(['false','true'][y > 2 and g < 4 and n != 3])
output:
true
Now, if you want you can store the result in a variable :
x=['false','true'][y > 2 and g < 4 and n != 3]
which you wanted i think.

Conditional list comprehension

I am trying to gain a better understanding of how list comprehensions work.
I have the following function that returns true or false if a number is prime (which I found somewhere on the forum but cant remember where):
import math
def is_prime(n):
if n % 2 == 0 and n > 2:
return False
for i in range(3, int(math.sqrt(n)) + 1, 2):
if n % i == 0:
return False
return True
if I run:
[x for x in range(2, num) if is_prime(x)]
i get the desired result
[2, 3, 5, 7, 11, 13, 17, 19]
in trying to convert the whole function to a list comprehension I came up with:
[x if not (x%2==0 and x > 2) else x for x in range (2, num)
for i in range(3, int(x**(1/2))+1, 2) if not (x%i==0)]
which produces:
[10, 11, 13, 14, 16, 17, 19]
not sure where I am going wrong and would appreciate some help. In truth I think it is better to use the function in this case, but like I said I am trying to understand the list comprehension and its capabilities.
You can do this:
[n for n in range(2, num) if n % 2 != 0 and n > 2 and all(n % i != 0 for i in range(3, int(math.sqrt(n)) + 1, 2))]
although one liner just for the sake of it is not necessarily a good thing. IMO using a prime tester function like you do is better...
NOTE: what doesn't work in your try is that you modified the logic of your outer list comprehension. You still want a structure like [n for n in range(...) if (expression testing if n is prime)].
The main problem is that you're making a convoluted translation. Try simplifying the original condition so that you return "x if x==2, else x ..." as you've already done. I'm just giving you a leading hint here; from what you've done already, I think you can figure it out.
If not, try simplifying the "else" expression so you learn the logic of that clause. Then I'm sure you'll get it. You're doing well.
There are logical errors in the code above
1/2 evaluates to 0, not to 0.5. To do that, either use 0.5 or 1/2.0 or 1.0/2
Also it does not take care of 2 as a special case. as (for i in range(3, int(x**(1/2.0))+1, 2) if not (x%i==0)) is not executed
for i in range(3, int(x**(1/2.0))+1, 2) if not (x%i==0) is also a logical error as whenever this condition is true, it causes multiple entries to be added
the correct way would be
[x for x in range (2, num) if x == 2 or (x > 2 and x % 2 == 1 and len([i for i in range(3, int(x**(1/2.0))+1, 2) if x%i == 0 ])==0)]

Categories

Resources