Chaining *= += operators - python

I have the following code:
aaa = np.random.rand(20, 1)
aaa *= 200
aaa -= 100
I wonder if it is possible to chain *= and -= operators on the same line. So, the loop over the array would be done only one time and I suppose a slight gain in performance results (of course for big arrays).

You cannot chain assignments in Python the way you can in C.
That is because in C an assignment is an expression: it has a value that can be assigned to a variable, or used in another expression. C got this idea from Algol, and those who come from the Pascal tradition tend to regard it as a misfeature. Because...
It is a trap for unwary novices who code if (a = b + c) when they mean if (a == b + c). Both are valid, but generally the second one is what you meant, because the first assigns the value of b + c to a and then tests the truth value of a.
Because assignments are not expressions in Python but statements, you will get a syntax error for if (a = b + c). It's just as invalid as if (return).
If you want to achieve what the C idiom does you can use an assignment expression (new in 3.8). You can explicitly code if (a := b + c) if what you really want to do is assign the value of b + c to a and then test the truth value of a (though technically I believe it actually tests the truth value of b + c; which comes to the same thing).
[And to the style martinets, yes, I do know that parens are redundant in a Python if statement.]

Doing them in one line would simply be
aaa = (aaa * 200) - 100
Though I doubt you'll see any performance difference between this version and what you wrote.

Related

Why is the "1" after sum necessary to avoid a syntax error

Why does this work:
def hamming_distance(dna_1,dna_2):
hamming_distance = sum(1 for a, b in zip(dna_1, dna_2) if a != b)
return hamming_distance
As opposed to this:
def hamming_distance(dna_1,dna_2):
hamming_distance = sum(for a, b in zip(dna_1, dna_2) if a != b)
return hamming_distance
I get this error:
Input In [90]
hamming_distance = sum(for a, b in zip(dna_1, dna_2) if a != b)
^
SyntaxError: invalid syntax
I expected the function to work without the 1 after the ()
The working expression can be unrolled into something like this:
hamming_distance = 0
for a, b in zip(dna_1, dna_2):
if a != b:
hamming_distance += 1
Without a number after +=, what should Python add? It doesn't know, and neither do we.
If this "unrolled" syntax or your code's relationship to it is new to you, probably start by reading up on list comprehensions, which generalize into generator expressions (which is what you have).
You wrote a generator expression. Generator expressions must produce a value (some expression to the left of the first for). Without it, you're saying "please sum all the lack-of-values not-produced by this generator expression".
Ask yourself:
What does a genexpr that produces nothing even mean?
What is sum summing when it's being passed a series of absolute nothing?
You could write a shorter genexpr with the same effect with:
hamming_distance = sum(a != b for a, b in zip(dna_1, dna_2))
since bools have integer values of 1 (for True) and 0 (for False), so it would still work, but it would be slower than sum(1 for a, b in zip(dna_1, dna_2) if a != b) (which produces fewer values for sum to work on and, at least on some versions of Python, allows sum to operate faster, since it has a fast path for summing small exact int types that bool breaks).

Order of evaluation of assignment expressions (walrus operator)

I have the following expression:
>>> a = 3
>>> b = 2
>>> a == (a := b)
False
Now, a == 2 after the operation, as expected. And the result is what I would want, i.e., comparison of a to RHS of assignment before assignment.
Reversing the order of the equality operator reverses the result:
>>> a = 3
>>> b = 2
>>> (a := b) == a
True
There does not appear to be anything directly relevant to this corner case in PEP-572, relative precedence section. The next section, change to evaluation order mentions that the evaluation order is left-to-right. Is that what is going on here (stash the value of a, update it, and compare vs update a, then compare against its new value)?
Where is this behavior defined, and how reliable is it?
Neither of those PEP sections have to do with this. You just have a == comparison, and the general Evaluation order applies: "Python evaluates expressions from left to right."
So your (a := b) == a simply first evaluates the left side (a := b), assigning something to a and evaluating to the same value. And then evaluate the right side a, which is of course still the same (just-assigned) value, so you get True.
About those PEP sections:
What that first PEP section says is that := groups less tightly than ==, so it would apply if you didn't have parentheses:
a == a := b would mean (a == a) := b (you'd get a syntax error for trying to assign to a comparison).
a := b == a would mean a := (b == a), where with your values the b == a evaluates to False and that gets assigned to a and becomes the result of the entire expression. (Note that at statement-level, you'd have to write (a := b == a).)
What that second PEP section does is just to point out something bad that had already existed but which the := made "more visible", so they suggested to finally fix it. The issue was that a dict comprehension like {X: Y for ...} evaluated Y before X, against the general left-to-right rule and against dict displays like {X: Y} which already did evaluate X before Y as expected. Consider this:
>>> a, b = 3, 2
>>> {a: (a := b) for _ in '_'}
{3: 2}
With that old behavior, it would've resulted in {2: 2}. And given that people might write something like that when := became available, it became more of an issue.

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)):
...

What does it mean that Python comparison operators chain/group left to right?

The Python documentation for operator precedence states:
Operators in the same box group left to right (except for
comparisons, including tests, which all have the same precedence and
chain from left to right — see section Comparisons...)
What does this mean? Specifically:
"Operators in the same box group left to right (except for
comparisons...)" -- do comparisons not group left to right?
If comparisons do not group left to right, what do they do instead? Do they "chain" as opposed to "group"?
If comparisons "chain" rather than "group", what is the difference between "chaining" and "grouping"?
What would be some examples to demonstrate that the comparison operators chain from left to right rather than from right to left?
Grouping (this is what non-comparison operators do):
a + b + c means (a + b) + c
Chaining (this is what comparison operators do):
a < b < c means (a < b) and (b < c)
Grouping left to right (this is the way things are grouped):
5 - 2 - 1 means (5 - 2) - 1 == 2
as opposed to grouping right to left (this would produce a different result):
5 - (2 - 1) == 4
Chaining left to right
Chaining is left to right, so in a < b < c, the expression a < b is evaluated before b < c, and if a < b is falsey, b < c is not evaluated.
(2 < 1 < f()) gives the value False without calling the function f, because 2 < 1 evaluates to false, so the second comparison does not need to be performed.
f() > 1 > g() calls f() in order to evaluate the first comparison, and depending on the result, it might or might not need to evaluate the second condition, which requires calling g().
NB. Each operand is evaluated at most once. So in the expression 1 < f() < 2, the function f() is only called once, and the value it gives is used in both comparisons (if necessary).
https://en.wikipedia.org/wiki/Short-circuit_evaluation
In fact, the chain behavior is not so obvious.
a == b == c
although one would expect this to be converted to
a == b and b == c
it is in fact converted into somthing similar to
b == c if a == b else False
which is a bit confusing if one tries to override the behavior of the comparison operators and chain them.

Python for loop doesn't have expected results

I got a bit stuck with a for loop - what I can see it is doing appears correct but isn't exactly what I'm trying to accomplish with it. I've come from a C background but any advice here would be beneficial.
def deal(player_num, cards):
a = 0
z = 0
i = 0
b = 0
c = player_num
hand = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
for a in range(player_num):
hand[a] = cards[i] + cards[i+b+c]
b == b+1
i == i+1
z == z+1
return hand
So the for a in range(player_num) seems to be working (appends a++) but hand[0], hand[1], etc. gets the same hand. I guess it loops a but not the other variables, so I need to use more than 1 nested loop to get i++, b++ and c++?
b == b+1 is a logical expression (returning False every time), not an assignment. I'm guessing you want something like: b += 1
== is the equality operator in Python. = is the assignment operator.
== checks whether its left operand and its right operand are equal and return True or False accordingly. b and b+1 will never be equal to each other and either way it does not make sense to perform an operation without side-effect (like comparing two values for equality) and then do nothing with its result.
If you want to change the values of your variables, use the assignment operator = instead of ==.

Categories

Resources