Using a string as an operator? - python

For example, let's say I have a bunch of assignment statements like this:
if operator == '==':
if a == b:
c += 5
elif operator == '>':
if a > b:
c += 5
elif operator == '<':
if a < b:
c += 5
The if nested if statements and assignments I gave are just examples but in the program I'm writing, they are really long. Just a small change is present where the operators differ, so I don't want to have to repeat the same long piece of code over and over again just for all these conditions. There are too many conditions and the code will get repeated many times..so is there a "faster" way to do this? Can I maybe define a string as an operator? Or any better ways?

How about:
from operator import *
str_ops = {'<':lt,'>':gt,'==':eq} # etc
op = str_ops.get(my_operator) #where my_operator was previously your 'operator'
assert op is not None #or raise your own exception
if op(a,b):
c+=5
And, if you wanted to gracefully handle a bogus operator in my_operator, you can do:
op = str_ops.get(my_operator, lambda x,y: None) #fallback: do-nothing operator
Bonuses to this approach:
ONE if statement. No matter how many operators you're handling.
O(1) behavior, as opposed to O(n) with branching if/elif statements.
The dict is very declarative: this string goes to this operator.
Doesn't Repeat Yourself.

You can use and and or operators effectively, like this
if (operator == '==' and a == b) or (operator == '>' and a > b) \
or (operator == '<' and a < b):
c += 5

As an alternative solution, if you can trust the source of the operator string (or have some way of validating it) you can use eval. But you have to be really careful when using eval; it can be a big security risk if you don't trust the source of the input.
if eval("a %s b" % operator):
c += 5

Related

Why "if-else-break" breaks in 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.

Is there a neat way to perform XOR operation on two conditions in IF statement?

I am currently creating a binary calculator which allows for both positive and negative binary inputs. I have the following code regarding my question:
if (firstvalue[0] == "-" and not secondvalue[0] == "-") or (secondvalue[0] == "-" and not firstvalue[0] == "-"):
invertedbinary.append("-")
So obviously, if either number is negative but not both then the final string will have a negative sign. Otherwise, both will be positive and there will be no negative sign on the string.
I'm just wondering if there is a neater way to do this? I tried using ^ but I guess its only a bitwise operator.
if firstvalue[0] == "-" ^ secondvalue[0] == "-":
I also tried xor incase of the off chance but obviously no luck. Any suggestions on a a neater way to do this?
^ will work just fine if you use parentheses:
if (firstvalue[0] == "-") ^ (secondvalue[0] == "-"):
You can also use != in the place of ^. It works the exact same way here, but may be a little more clear.
One thing to remember with ^ is that it will behave unexpectedly if any of the expressions is a non-bool: a ^ b does not equal (a and not b) or (b and not a) if, say a = 5 and b = 2!
Since xor can't short-circuit, you can as well use a function.
from operator import xor as xor_
from functools import reduce
def xor(*args):
return reduce(xor_, map(bool, args))
if xor(firstvalue[0] == '-', secondvalue[0] == '-'):
...
This works for any number of values, and non-boolean values too, so you can do xor(1, 1, 1, 1) = 0 and xor(1, 1, 1, 1, 1) = 1

Compact way of writing (a + b == c or a + c == b or b + c == a)

Is there a more compact or pythonic way to write the boolean expression
a + b == c or a + c == b or b + c == a
I came up with
a + b + c in (2*a, 2*b, 2*c)
but that is a little strange.
If we look at the Zen of Python, emphasis mine:
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than right now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
The most Pythonic solution is the one that is clearest, simplest, and easiest to explain:
a + b == c or a + c == b or b + c == a
Even better, you don't even need to know Python to understand this code! It's that easy. This is, without reservation, the best solution. Anything else is intellectual masturbation.
Furthermore, this is likely the best performing solution as well, as it is the only one out of all the proposals that short circuits. If a + b == c, only a single addition and comparison is done.
Solving the three equalities for a:
a in (b+c, b-c, c-b)
Python has an any function that does an or on all the elements of a sequence. Here I've converted your statement into a 3-element tuple.
any((a + b == c, a + c == b, b + c == a))
Note that or is short circuiting, so if calculating the individual conditions is expensive it might be better to keep your original construct.
If you know you're only dealing with positive numbers, this will work, and is pretty clean:
a, b, c = sorted((a, b, c))
if a + b == c:
do_stuff()
As I said, this only works for positive numbers; but if you know they're going to be positive, this is a very readable solution IMO, even directly in the code as opposed to in a function.
You could do this, which might do a bit of repeated computation; but you didn't specify performance as your goal:
from itertools import permutations
if any(x + y == z for x, y, z in permutations((a, b, c), 3)):
do_stuff()
Or without permutations() and the possibility of repeated computations:
if any(x + y == z for x, y, z in [(a, b, c), (a, c, b), (b, c, a)]:
do_stuff()
I would probably put this, or any other solution, into a function. Then you can just cleanly call the function in your code.
Personally, unless I needed more flexibility from the code, I would just use the first method in your question. It's simple and efficient. I still might put it into a function:
def two_add_to_third(a, b, c):
return a + b == c or a + c == b or b + c == a
if two_add_to_third(a, b, c):
do_stuff()
That's pretty Pythonic, and it's quite possibly the most efficient way to do it (the extra function call aside); although you shouldn't worry too much about performance anyway, unless it's actually causing an issue.
If you will only be using three variables then your initial method:
a + b == c or a + c == b or b + c == a
Is already very pythonic.
If you plan on using more variables then your method of reasoning with:
a + b + c in (2*a, 2*b, 2*c)
Is very smart but lets think about why. Why does this work?
Well through some simple arithmetic we see that:
a + b = c
c = c
a + b + c == c + c == 2*c
a + b + c == 2*c
And this will have to hold true for either a,b, or c, meaning that yes it will equal 2*a, 2*b, or 2*c. This will be true for any number of variables.
So a good way to write this quickly would be to simply have a list of your variables and check their sum against a list of the doubled values.
values = [a,b,c,d,e,...]
any(sum(values) in [2*x for x in values])
This way, to add more variables into the equation all you have to do is edit your values list by 'n' new variables, not write 'n' equations
The following code can be used to iteratively compare each element with the sum of the others, which is computed from sum of the whole list, excluding that element.
l = [a,b,c]
any(sum(l)-e == e for e in l)
Don't try and simplify it. Instead, name what you're doing with a function:
def any_two_sum_to_third(a, b, c):
return a + b == c or a + c == b or b + c == a
if any_two_sum_to_third(foo, bar, baz):
...
Replace the condition with something "clever" might make it shorter, but it won't make it more readable. Leaving it how it is isn't very readable either however, because it's tricky to know why you're checking those three conditions at a glance. This makes it absolutely crystal clear what you're checking for.
Regarding performance, this approach does add the overhead of a function call, but never sacrifice readability for performance unless you've found a bottleneck you absolutely must fix. And always measure, as some clever implementations are capable of optimizing away and inlining some function calls in some circumstances.
Python 3:
(a+b+c)/2 in (a,b,c)
(a+b+c+d)/2 in (a,b,c,d)
...
It scales to any number of variables:
arr = [a,b,c,d,...]
sum(arr)/2 in arr
However, in general I agree that unless you have more than three variables, the original version is more readable.
(a+b-c)*(a+c-b)*(b+c-a) == 0
If the sum of any two terms is equal to the third term, then one of the factors will be zero, making the entire product zero.
How about just:
a == b + c or abs(a) == abs(b - c)
Note that this won't work if variables are unsigned.
From the viewpoint of code optimization (at least on x86 platform) this seems to be the most efficient solution.
Modern compilers will inline both abs() function calls and avoid sign testing and subsequent conditional branch by using a clever sequence of CDQ, XOR, and SUB instructions. The above high-level code will thus be represented with only low-latency, high-throughput ALU instructions and just two conditionals.
The solution provided by Alex Varga "a in (b+c, b-c, c-b)" is compact and mathematically beautiful, but I wouldn't actually write code that way because the next developer coming along would not immediately understand the purpose of the code.
Mark Ransom's solution of
any((a + b == c, a + c == b, b + c == a))
is more clear but not much more succinct than
a + b == c or a + c == b or b + c == a
When writing code that someone else will have to look at, or that I will have to look at a long time later when I have forgotten what I was thinking when I wrote it, being too short or clever tends to do more harm than good. Code should be readable. So succinct is good, but not so succinct that the next programmer can't understand it.
Request is for more compact OR more pythonic - I tried my hand at more compact.
given
import functools, itertools
f = functools.partial(itertools.permutations, r = 3)
def g(x,y,z):
return x + y == z
This is 2 characters less than the original
any(g(*args) for args in f((a,b,c)))
test with:
assert any(g(*args) for args in f((a,b,c))) == (a + b == c or a + c == b or b + c == a)
additionally, given:
h = functools.partial(itertools.starmap, g)
This is equivalent
any(h(f((a,b,c))))
I want to present what I see as the most pythonic answer:
def one_number_is_the_sum_of_the_others(a, b, c):
return any((a == b + c, b == a + c, c == a + b))
The general case, non-optimized:
def one_number_is_the_sum_of_the_others(numbers):
for idx in range(len(numbers)):
remaining_numbers = numbers[:]
sum_candidate = remaining_numbers.pop(idx)
if sum_candidate == sum(remaining_numbers):
return True
return False
In terms of the Zen of Python I think the emphasized statements are more followed than from other answer:
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than right now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
As an old habit of my programming, I think placing complex expression at right in a clause can make it more readable like this:
a == b+c or b == a+c or c == a+b
Plus ():
((a == b+c) or (b == a+c) or (c == a+b))
And also I think using multi-lines can also make more senses like this:
((a == b+c) or
(b == a+c) or
(c == a+b))
In a generic way,
m = a+b-c;
if (m == 0 || m == 2*a || m == 2*b) do_stuff ();
if, manipulating an input variable is OK for you,
c = a+b-c;
if (c==0 || c == 2*a || c == 2*b) do_stuff ();
if you want to exploit using bit hacks, you can use "!", ">> 1" and "<< 1"
I avoided division though it enables use to avoid two multiplications to avoid round off errors. However, check for overflows
def any_sum_of_others (*nums):
num_elements = len(nums)
for i in range(num_elements):
discriminating_map = map(lambda j: -1 if j == i else 1, range(num_elements))
if sum(n * u for n, u in zip(nums, discriminating_map)) == 0:
return True
return False
print(any_sum_of_others(0, 0, 0)) # True
print(any_sum_of_others(1, 2, 3)) # True
print(any_sum_of_others(7, 12, 5)) # True
print(any_sum_of_others(4, 2, 2)) # True
print(any_sum_of_others(1, -1, 0)) # True
print(any_sum_of_others(9, 8, -4)) # False
print(any_sum_of_others(4, 3, 2)) # False
print(any_sum_of_others(1, 1, 1, 1, 4)) # True
print(any_sum_of_others(0)) # True
print(any_sum_of_others(1)) # False
There is little to gain with such a small expression but using a function just to not having to repeat the summation and comparison could be an option. It makes it a bit more maintainable when wanting to change the operation to something like a + b == c * 2.
def equals_sum(a, b, c):
return a + b == c
if (equals_sum(a, b, c)
or equals_sum(a, c, b)
or equals_sum(b, c, a)):
...

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.

What are the rules regarding chaining of "==" and "!=" in Python

This morning, I find myself writing something like:
if (a == b == c):
# do something
And was surprised that it gave me the expected result.
I thought it would behave as:
if ((a == b) == c):
# do something
But it obviously didn't. It seems Python is treating the first statement differently from the second, which is nice but I couldn't find any documentation or explanation regarding this.
I tested and got this:
In [1]: 2 == 2 == 2
Out[1]: True
In [2]: (2 == 2) == 2
Out[2]: False
Would someone care to explain me what are the rules regarding such "chaining" of == (or !=) ?
Thank you very much.
This works with all comparison operators - eg, you can also do:
>>> 4 < 5 < 6
True
>>> 4 < 5 !=2
True
In general, according to the documentation, a op1 b op2 c where op1 and op2 are any of: <, >, !=, ==, <=, >=, is , is not, in or not in will give the same result as:
a op1 b and b op2 c
The docs also say that this can work with arbitrarily many comparisons, so:
>>> 5 != '5' != 'five' != (3+2)
True
Which can be a slightly confusing result sometimes since it seems to say 5 != (3+2) - each operand is only compared with the ones immediately adjacent to it, rather than doing all possible combinations (which mightn't be clear from examples using only ==, since it won't affect the answer if everything defines __eq__ sanely).
As far as I know the example you point out isn't chaining.
2 == 2 == 2 is like (2 == 2) and ( 2 == 2) which turns out to be True and True
while
(2 == 2) == 2 is like (True) == 2
Check here: http://docs.python.org/reference/expressions.html#not-in

Categories

Resources