Python: shortcut conditional expression - python

the shortcut conditional expression :
expression1 if condition else expression2
x=1 if a>3 else 2
But: can I have 2 expressions at the start ?
x=1,b=3 if a>3 else 2
Thanks to > idontknow, solution is >
previousTime,BS_Count=(db_row_to_list[0][14],BS_Count+1) if db_row_to_list[0][14] is not None else (db_row_to_list[0][3],BS_Count)

Not quite. For something this, it would be possible to use an if statement.
if a > 3:
x = 1
b = 3
else:
x = 2
b = None
If you want everything to become a oneliner, you can use tuple unpacking in Python. What tuple unpacking does is basically take the elements from a tuple and store them as variables, instead of elements of a tuple.
An application of this concept would be something like this:
x, b = (1, 3) if a > 3 else (2, None)
Note that it is a oneliner! 🤗
EDIT: To answer your question in the updated context:
You can use the following, shorter code. I think the effect will be the same.
a = 3
b = 7
c = 6
a, b = (8, b+1) if c > 3 else (5, b)
print(a, b)

In your example:
x=1 if a>3 else 2
The conditional expression part is:
1 if a>3 else 2
So, you're assigning the result of that conditional expression to x.
In your second example, this is invalid syntax:
x=1,b=3 if a>3 else 2
That's because it's equivalent to:
(1,b)=3 if a>3 else 2
Or, as a simpler example:
(1,b)=3
This is invalid syntax, because you can't do something like 1 = 3 in Python, which is what that code is trying to do. Although if both elements of the tuple were not literals, you'd get a different error:
(b,a)=3
TypeError: cannot unpack non-iterable int object
So, if you want to do multiple assignments with a conditional expression, you can have the conditional expression return a tuple, and have your left-hand side be a tuple of the two variables you want to assign values to:
x,b=(1, 3) if a>3 else (2, 2)
This is equivalent to:
if a > 3:
x = 1
b = 3
else:
x = 2
b = 2
Which is what I assume you intended with your original code.

You can use tuples:
>>> a = 4
>>> (x, b) = (1, 3) if a > 3 else (2, 2)
>>> x
1
>>> b
3
>>>

The statement x = 1, b = 3 if a > 3 else 2 is not valid. You can still have a one-liner though:
x, b = 1 if a > 3 else 2, 3 if a > 3 else 2
This sets the value of x to 1 if a > 3 else 2 and b to 3 if a > 3 else 2

For context : This is the actual code >
if db_row_to_list[0][14] is not None:
previousTime=db_row_to_list[0][14]
BS_Count+=1
else:
db_row_to_list[0][3]
I can do this though : ie. use a function > but is it any better??
num=3
num1=7
def doIt():
print("doIt()")
global num,num1
num=8
num1+=1
return num
a=6
num=doIt() if a>3 else 5
print(num," ",num1)

Related

Python IF statement to a single line

Is it possible to put this code into a single line?
if x == 0:
a += j["sum"]
elif x == 1:
b += j["sum"]
e.e. :D
This is not a working example just for demonstration purposes
a += j["sum"] if x == 0 else b += j["sum"]
You can do it this way if you have Python 3.8 or later for the assignment expression operator :=:
(a := a + j["sum"]) if x == 0 else (b := b + j["sum"]) if x == 1 else None
but really the original is best. It's preferable that code is clear and straightforward.
I may suggest you this one-line dual assignment:
a, b = a + (j["sum"] if x == 0 else 0), b + (j["sum"] if x == 1 else 0)
But you can also use the old good semi-colon to perform two instructions on one line:
a += (j["sum"] if x == 0 else 0); b += (j["sum"] if x == 1 else 0)
I would strongly discourage it, but I guess one could use boolean true as the multiplication identity to do:
a, b = a + (x==0) * j["sum"], b + (x==1) * j["sum"]
This seems to work as I expect in a little loop
a=100
b=200
jsum=10
for x in range(3):
a, b = a + (x==0) * jsum, b + (x==1) * jsum
print(a, b)
Giving:
110 200
110 210
110 210
While I would not code like this myself, one might throw caution to the wind and leverage exec():
exec(f"{['a','b'][x]} += j['sum']" if x in [0,1] else "pass")
or potentially even better (credit to #wjandrea):
if x in {0, 1}: exec(f"{['a','b'][x]} += j['sum']")
Note, if you know x is always either 0 or 1 this would simplify to:
exec(f"{['a','b'][x]} += j['sum']")
Your pseudo-code solution suggests that might be so but your questions suggests it is not.
example:
a=100
b=200
jsum=10
for x in range(3):
exec(f"{['a','b'][x]} += jsum" if x in [0,1] else "pass")
print(a, b)
giving:
110 200
110 210
110 210
#wjandrea has suggested a nice improvement that produces the same results and is likely faster given the ability to bypass exec() if x is out of range.
a=100
b=200
jsum=10
for x in range(3):
if x in {0, 1}: exec(f"{['a','b'][x]} += jsum")
print(a, b)

Clear mathematical way to cast int value to 0 or 1

Maybe it's too trivial question but I can't find answer if there is a some more elegant way to cast int value to 0 or 1 without using condition or type casting?
Now I have only following variants and both are ugly:
x = 5
a = some_int_value * (1 if x > 0 else 0)
b = some_int_value * int(bool(x))
ADDED
x is a non-negative value.
To paraphrase your condition, x can be 0 or a value larger than 0. If x > 0, you want to use the value some_int_value, otherwise you want 0 (which is identical to x). Then do:
c = x and some_int_value
If x is 0, i.e. falsey, the and expression returns x. If x is truthy (non-zero), it returns some_int_value.
Arguably even more comprehensible would be:
d = some_int_value if x > 0 else 0
Or:
e = 0
if x > 0:
e = some_int_value
Another way without functions, casting, or conditionals, but with comparisons:
d = some_int_value * (x > 0)
However, in terms of readability, a ternary ... if ... else ... should probably be preferred.
If you want a purely mathematical way, you can use exponentiation with 1 - 0x, since 0**x is 1 only if x == 0 and 0 otherwise. But whether that is in any way clear is another question. (Also note that this gives a division-by-zero for x < 0, but you said that you don't have those.)
>>> x = 5
>>> 1 - 0**x
1
>>> x = 0
>>> 1 - 0**x
0

problem with arrays and operations result beign asaigned to values

Here is the python code :
x=[2, 3, 5, 7]
for i in range(1, 5000):
if i%2:
if i%3:
if i%5:
if i%7:
x.append(i)
x.remove(1)
a = 6
b = 2
for i in range(0, 10):
a = x[a - b]
b = x[a] - x[b]
I get an IndexError: list index out of range for some reason, even though x[] is 1360 and a is just 6 while b is 2 so I dont know why it is happening. Any help would be nice.
I am using the python shell. Would that be a problem?
The problem of your code is your logic in the for loop.
You see:-
a = 6
b = 2
for i in range(0, 10):
a = x[a - b]
b = x[a] - x[b]
Yes a was 6 & b was 2, but then when you enter your for loop for the first time,
a's value is
x[a-b] which is x[6-2]
i.e. x[4] which gives you 11
so a's value is 11, likewise for b,
b = x[a] - x[b] # which translates to x[11] - x[2]
which becomes b=32 & the loop goes on jumping/changing the values of a & b which leads you to IndexError: list index out of range which is expected.
Try executing your program in pycharm & debug it, you would understand it better, or maybe just put some print statements.
I could not type so much in a comment, hence posted it as an answer.

lambda with nested if's weird syntax

I like using lambdas more then I should so more than once I wanted to solve nested if statement with it and failed using standard syntax.
But then I tried something like this:
lambda x: 1 if x > 100 else 2 if x > 50 else 3
Surprisingly it works, although proper function is supposed to have elif:
def foo(x):
if x > 100:
return 1
elif x > 50:
return 2
else:
return 3
Any ideas why?
You don't have to use elif. You can use if inside another else which is the same as what lambda function is doing:
def foo(x):
if x > 100:
return 1
else:
if x > 50:
return 2
else:
return 3
I'd recommend
lambda x: 1 if x > 100 else (2 if x > 50 else 3)
for readability because explicit is better than implicit.
Indeed, due to evaluation from left to right, the ternary operator is right associative as the following code demonstrates
def f(a, b):
return 1 if a else 2 if b else 3
def left(a, b):
return (1 if a else 2) if b else 3
def right(a, b):
return 1 if a else (2 if b else 3)
tpl = "{:^8}{:^8}{:^6}{:^6}{:^6}"
print(tpl.format('a', 'b', 'f', 'left', 'right'))
for (a, b) in [(False, False),(False, True),(True, False),(True, True)]:
print(tpl.format(repr(a), repr(b), f(a,b), left(a,b), right(a,b)))
""" my output ->
a b f left right
False False 3 3 3
False True 2 2 2
True False 1 3 1
True True 1 1 1
"""
The ternary expression always gives the same result as the expression where everything after the first else is parenthesized.
When the conditions are (x>100) and (x>50), the case True, False can never happen, so the three expressions give the same result. Nevertheless, explicit is better than implicit!
You are actually using something like elif by writing
else 2 if x > 50
in
lambda x: 1 if x > 100 else 2 if x > 50 else 3
Its just a different way of writing something like elif in lambda
lambda x: 1 if x > 100 else 2 if x > 50 else 3
Means :
# 1 if x > 100
if x > 100:
return 1
# else 2 if x > 50 else 3
else:
# 2 if x > 50 else 3
if x > 50:
return 2
else:
return 3
Which does the same thing that your second snippet with elif.
You cannot use elif in a lambda expression. As far as I know, it should raise a SyntaxError.
You have concatenated two conditional expressions.
One
2 if x > 50 else 3
provides the value for the else clause of the other.
1 if x > 100 else ...
You have the conditional expression
x if C else y
where
x = 1
C = 100
and
y = 2 if x > 50 else 3
From the docs, y can be an expression so y can be a conditional expression.

Looping through an interval in either direction

Suppose you want to loop through all integers between two bounds a and b (inclusive), but don't know in advance how a compares to b. Expected behavior:
def run(a, b):
if a < b:
for i in range(a, b + 1):
print i,
elif a > b:
for i in range(a, b - 1, -1):
print i,
else:
print a
print
run(3, 6)
run(6, 3)
run(5, 5)
Result:
3 4 5 6
6 5 4 3
5
Is there a more elegant solution? The following is more concise, but fails when a == b:
def run(a, b):
for i in range(a, b + cmp(b, a), cmp(b, a)):
print i,
print
run(3, 6)
run(6, 3)
run(5, 5)
Result:
3 4 5 6
6 5 4 3
(...)
ValueError: range() step argument must not be zero
This will work for all cases:
def run(a, b):
"""Iterate from a to b (inclusive)."""
step = -1 if b < a else 1
for x in xrange(a, b + step, step):
yield x
The insight that led me to this formulation was that step and the adjustment to b were the same in both of your cases; once you have an inclusive end you don't need to special-case a == b. Note that I've written it as a generator so that it doesn't just print the results, which makes it more use when you need to integrate it with other code:
>>> list(run(3, 6))
[3, 4, 5, 6]
>>> list(run(6, 3))
[6, 5, 4, 3]
>>> list(run(5, 5))
[5]
Using generator delegation in Python 3.3+ (see PEP-380), this becomes even neater:
def run(a, b):
"""Iterate from a to b (inclusive)."""
step = -1 if b < a else 1
yield from range(a, b + step, step)
You almost got it yourself:
def run(a, b):
for i in range(a, b + (cmp(b, a) or 1), cmp(b, a) or 1):
print i,
print
works just fine... when cmp(b, a) evaluates to 0 (when they are equal), it defaults to 1, although I'd definitely consider Jon's answer more elegant, you were on the right track! I make extensive use of python's logical or when doing comparisons like this:
(func() or default) is very useful for any function that returns a zero that you want to overwrite. Python evaluates it as False or True and returns default.
range(min((a,b)), max((a,b))+1)

Categories