Check if multiple variables have the same value - python

I have a set of three variables x, y, z and I want to check if they all share the same value. In my case, the value will either be 1 or 0, but I only need to know if they are all the same. Currently I'm using
if 1 == x and 1 == y and 1 == z:
sameness = True
Looking for the answer I've found:
if 1 in {x, y, z}:
However, this operates as
if 1 == x or 1 == y or 1 == z:
atleastOneMatch = True
Is it possible to check if 1 is in each: x, y, and z?
Better yet, is there a more concise way of checking simply if x, y, and z are the same value?
(If it matters, I use Python 3.)

If you have an arbitrary sequence, use the all() function with a generator expression:
values = [x, y, z] # can contain any number of values
if all(v == 1 for v in values):
otherwise, just use == on all three variables:
if x == y == z == 1:
If you only needed to know if they are all the same value (regardless of what value that is), use:
if all(v == values[0] for v in values):
or
if x == y == z:

To check if they are all the same (either 1 or 2):
sameness = (x == y == z)
The parentheses are optional, but I find it improves readability

How about this?
x == y == z == 1

In my case, the value will either by 1 or 2, but I only need to know if they are all the same
Is it possible to check if 1 is in each: x, y, and z?
Better yet, is there a more concise way of checking simply if x, y, and z are the same value?
Sure:
x == y == z
which is equivalent to
(x == y) and (y == z)
If you have an arbitrary (nonzero) number of values to compare:
all(values[0] == v for v in values[1:])

You could use something similar to what you have:
sameness = (len({x, y, z}) == 1)
This allows for any number of variables. For example:
variables = {x, y, z, a, b, ...}
sameness = (len(variables) == 1)
Note: Creating a set means that each variable needs to be hashed and the hashed values need to be stored, but all() with a generator expression is short-circuiting and keeps track of only two values at a time. Therefore, besides its readability, the generator expression is more efficient.

Another way:
sameness = all(e == 1 for e in [x, y, z])

[x,y,z].count(1)
will count how many variables have 1 as value

Below all() and any() functions in python can server the purpose.
all() act as "AND": if all the values in any ittertable object is equal to given condition value, then Return True else return False.
Examples
assert all(b == True for b in [True,True,True,True]) == True
assert all(b == True for b in [False,True,True,True]) == False
any() act as "OR": if any one value in any ittertable object is equal to given condition value, then Return True else return False.
Examples
assert any(b == True for b in [False,False,False,True]) == True
assert any(b == True for b in [False,False,False,False]) == False

If you had more variables, this way might be most concise:
{x} == {y, z}
(Not as fast as x == y == z, but the question asked for concise, not for fast.)

Related

Improving loop over 4 variables

I am trying to perform the following loop:
I have three arrays:
l, val_lambda, val_alpha, each of them contains 10 values
and a datasets, a list of 1000 class results, i.e dataset[i] = traj_analysis(dt, X, Y, x, y, z) for x, y, z in l, val_lambda, val_alpha.
Now I would like to print on a file one of the instance's result of the class traj_analysis - with a certain concern for the folders. This is what I wrote down:
the_vals = [(x, y, z) for x in val_lambda for y in val_alpha for z in l]
for (x, y, z) in tqdm(the_vals):
for data in datasets:
if x == data.par_lambda & y == data.par_alpha & z == data.l0:
filename_msd = './simulation/lamda =' +str(f'{x:.2f}')+'/ alpha ='+str(f'{y:.2f}')+'/cargo/l0='+str(f'{z:.2f}')+'/cargo_msd.csv'
os.makedirs(os.path.dirname(filename_msd), exist_ok=True)
np.savetxt(filename_msd, data.msd, delimiter=',')
Unfortunately I got this error:
ufunc 'bitwise_and' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
Can someone help me to reformulate the loop in a coherent way and maybe also to speed it up?
Firstly, Consider using zip instead,
for (x, y, z) in tqdm(zip(val_lambda, val_alpha, l)):
Secondly, the issue could be in your if statement logic.
This is because & has higher precedence over ==, so in your statement, it might have been evaluated as:
if x == (data.par_lambda & y) == (data.par_alpha & z) == data.l0:
The fix would be to put the appropriate bracket at the appropriate places:
if (x == data.par_lambda) & (y == data.par_alpha) & (z == data.l0):
Just to add on, & is more commonly used for bitwise operations, hence, as fferri suggested, you might want to change it to and instead. The and operator has lower precedence over ==, thus == will be evaluted first. Nonetheless, it's still good practice to have the brackets!
if (x == data.par_lambda) and (y == data.par_alpha) and (z == data.l0):

How to make list comprehensions more readable?

I have this bit of code below, and i think it's kinda hard to understand if you are new to python.
How would i go about making it more readable to a group of newcomers to python (students)
def right_inwrongplace(userGuess, number):
correct_places = [True if v == number[i] else False for i, v in enumerate(userGuess)]
g = [v for i, v in enumerate(userGuess) if not correct_places[i]]
n = [v for i, v in enumerate(number) if not correct_places[i]]
return len([i for i in g if i in n])
Here are a few improvements:
True if x else False is simply bool(x) or, as you are doing a comparison already, just that expression, i.e. v == number[i].
Since you are accessing the number by positional index you can just zip the two sequences.
So for the first you'd get:
correct_places = [x == y for x, y in zip(userGuess, number)]
The same argument with zip applies to the following two comprehensions (you can again iterate over the original string):
g = [x for x, y in zip(userGuess, number) if x != y]
n = [y for x, y in zip(userGuess, number) if x != y]
Given that this is basically the same comprehension two times, and that we don't need correct_places anymore we can instead do the following:
g, n = zip(*[(x, y) for x, y in zip(userGuess, number) if x != y])
Then you can sum instead of len:
return sum(x in n for x in g)
So basically you can use the following code:
g, n = zip(*(xy for xy in zip(userGuess, num) if xy[0] != xy[1])
return sum(x in n for x in g)

How to stop a loop?

def sum_div(x, y):
for k in range(x,y+1):
for z in range(x,y+1):
sx = 0
sy = 0
for i in range(1, k+1):
if k % i == 0:
sx += i
for j in range(1, z+1):
if z % j == 0:
sy += j
if sx == sy and k!= z:
print "(", k ,",", z, ")"
x = input("Dati x : ")
y = input("Dati y : ")
sum_div(x, y)
How do I stop the looping if the value of z == y?
The loops print a pair of numbers in a range from x to y, but when it hit the y value the loop prints a reverse pair of numbers that I don't need it to.
The break command will break out of the loop. So a line like this:
if (z == y):
break
should do what you want.
What you're think you are asking for is the break command, but what you're actually looking for is removal of duplication.
Your program lacks some clarity. For instance:
for i in range(1, k+1):
if k % i == 0:
sx += i
for j in range(1, z+1):
if z % j == 0:
sy += j
These two things are doing essentially the same thing, which can be written more cleanly with a list comprehension (in the REPL):
>>> def get_divisors(r: int) -> list:
... return [i if r % i == 0 else 0 for i in range(1, r+1)]
...
...
>>> get_divisors(4)
>>> [1, 2, 0, 4]
>>> sum(get_divisors(4))
>>> 7
Your line:
while y:
... will infinitely loop if you find a match. You should just remove it. while y means "while y is true", and any value there will evaluate as true.
This reduces your program to the following:
def get_divisors(r: int) -> list:
return [i if r % i == 0 else 0 for i in range(1, r+1)]
def sum_div(x, y):
for k in range(x,y+1):
sum_of_x_divisors = sum(get_divisors(k)) # Note this is moved here to avoid repeating work.
for z in range(x,y+1):
sum_of_y_divisors = sum(get_divisors(z))
if sum_of_x_divisors == sum_of_y_divisors and k!= z:
print("({},{})".format(k, z))
Testing this in the REPL it seems correct based on the logic of the code:
>>> sum_div(9,15)
(14,15)
(15,14)
>>> sum_div(21, 35)
(21,31)
(31,21)
(33,35)
(35,33)
But it's possible that for sum_div(9,15) you want only one of (14,15) and (15,14). However, this has nothing to do with breaking your loop, but the fact that what you're attempting to do has two valid values when k and z don't equal each other. This is demonstrated by the second test case, where (33,35) is a repeated value, but if you broke the for loop on (21,31) you would not get that second set of values.
One way we can account for this is by reordering when work is done:
def sum_div(x, y):
result_set = set() # Sets cannot have duplicate values
for k in range(x,y+1):
sum_of_x_divisors = sum(get_divisors(k))
for z in range(x,y+1):
sum_of_y_divisors = sum(get_divisors(z))
if sum_of_x_divisors == sum_of_y_divisors and k!= z:
result_set.add(tuple(sorted((k,z)))) # compile the result set by sorting it and casting to a tuple, so duplicates are implicitly removed.
for k, z in result_set: # Print result set after it's been compiled
print("({},{})".format(k, z))
And we see a correct result:
>>> sum_div(9,15)
(14,15)
>>> sum_div(21,35)
(21,31)
(33,35)
Or, the test case you provided in comments. Note the lack of duplicates:
>>> sum_div(10,25)
(16,25)
(14,15)
(15,23)
(10,17)
(14,23)
Some takeaways:
Break out functions that are doing the same thing so you can reason more easily about it.
Name your variables in a human-readable format so that we, the readers of your code (which includes you) understands what is going on.
Don't use loops unless you're actually looping over something. for, while, etc. only need to be used if you're planning on going over a list of things.
When asking questions, be sure to always include test input, expected output and what you're actually getting back.
The current best-practice for printing strings is to use the .format() function, to make it really clear what you're printing.

Usage of the "==" operator for three objects

Is there any computational difference between these two methods of checking equality between three objects?
I have two variables: x and y. Say I do this:
>>> x = 5
>>> y = 5
>>> x == y == 5
True
Is that different from:
>>> x = 5
>>> y = 5
>>> x == y and x == 5
True
What about if they are False?
>>> x = 5
>>> y = 5
>>> x == y == 4
False
And:
>>> x = 5
>>> y = 5
>>> x == y and x == 4
False
Is there any difference in how they are calculated?
In addition, how does x == y == z work?
Thanks in advance!
Python has chained comparisons, so these two forms are equivalent:
x == y == z
x == y and y == z
except that in the first, y is only evaluated once.
This means you can also write:
0 < x < 10
10 >= z >= 2
etc. You can also write confusing things like:
a < b == c is d # Don't do this
Beginners sometimes get tripped up on this:
a < 100 is True # Definitely don't do this!
which will always be false since it is the same as:
a < 100 and 100 is True # Now we see the violence inherent in the system!
Adding a little visual demonstration to already accepted answer.
For testing equality of three values or variables. We can either use:
>>> print(1) == print(2) == print(3)
1
2
3
True
>>> print(1) == print(2) and print(2) == print(3)
1
2
2
3
True
The above statements are equivalent but not equal to, since accesses are only performed once. Python chains relational operators naturally. See this docs:
Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).
If the functions called (and you are comparing return values) have no side-effects, then the two ways are same.
In both examples, the second comparison will not be evaluated if the first one evaluates to false. However: beware of adding parentheses. For example:
>>> 1 == 2 == 0
False
>>> (1 == 2) == 0
True
See this answer.

failing "Private Test Cases" in pyschools

I used the following script to try to answer this question:
def isEquilateral(x, y, z):
if x<0 or y <0 or z<0:
return False
elif x==y==z:
return True
else:
return False
It returned Private Test Cases, I don't know if this is a software bug, or my code really have some problem. Can anyone help? Thanks.
update 01
Question as below:
Write a function isEquilateral(x, y, z) that accepts the 3 sides of a triangle as arguments. The program should return True if it is an equilateral triangle.
Examples
>>> isEquilateral(2, 4, 3)
False
>>> isEquilateral(3, 3, 3)
True
>>> isEquilateral(-3, -3, -3)
False
While I'm not impressed with their feedback system, the problem is you return True when x, y, z == 0. A simpler solution:
def isEquilateral(x, y, z):
if x == y == z and x > 0:
return True
return False
And an even simpler solution, which shows off the beauty of Python (thanks, F.J!) using even more chained comparisons:
def isEquilateral(x, y, z):
return x == y == z > 0
This worked for me
def isEquilateral(x, y, z):
if (x == y or x == z) and x > 0:
return True
return False
This worked for me
def isEquilateral(x, y, z):
a = [x,y,z]
for e in a:
if e> 0:
if x==y==z:
return True
else:
return False
else:
return False

Categories

Resources