How to compare multiple variables to the same value? - python

I am using Python and I would like to have an if statement with many variables in it.
Such as:
if A, B, C, and D >= 2:
print (A, B, C, and D)
I realize that this is not the correct syntax and that is exactly the question I am asking — what is the correct Python syntax for this type of an if statement?

You want to test a condition for all the variables:
if all(x >= 2 for x in (A, B, C, D)):
print(A, B, C, D)
This should be helpful if you're testing a long list of variables with the same condition.
If you needed to check:
if A, B, C, or D >= 2:
Then you want to test a condition for any of the variables:
if any(x >= 2 for x in (A, B, C, D)):
print(A, B, C, D)

Another idea:
if min(A, B, C, D) >= 2:
print A, B, C, D

I'd probably write this as
v = A, B, C, D
if all(i >= 2 for i in v):
print v

If you have ten variables that you are treating as a group like this, you probably want to make them elements of a list, or values in a dictionary, or attributes of an object. For example:
my_dict = {'A': 1, 'B': 2, 'C': 3 }
if all(x > 2 for x in my_dict.values()):
print "They're all more than two!"

How about:
if A >= 2 and B >= 2 and C >= 2 and D >= 2:
print A, B, C, D
For the general case, there isn't a shorter way for indicating that the same condition must be true for all variables - unless you're willing to put the variables in a list, for that take a look at some of the other answers.

Depending on what you are trying to accomplish, passing a list to a function may work.
def foo(lst):
for i in lst:
if i < 2:
return
print lst

Except that she's probably asking for this:
if A >= 2 and B >= 2 and C >= 2 and D >= 2:

Related

Using arguments as variable names: Why does it solve this problem?

I'm going through some material about functions and I'm writing Python code to make some sense of the pseudocode examples.
The goal is printing the variables after I call the function, then check the new values.
def my_sum(x, y, z):
z = x + y
return x, y, z
A = 1
B = 2
C = 0
my_sum(A, B, C)
my_sum(B, C, A)
my_sum(C, A, B)
my_sum(A, B, C)
my_sum(B, C, A)
print(A, B, C)
My first instinct was to write this procedural approach, but when I do the calling the program won't give the right answer, because A, B and C aren't saving whatever is happening inside the function. So A is always 1, B is 2 and so forth
It turns out when I assign the calling with the arguments, the variables A, B and C receive the new values and they're now keeping it. Finally it prints 21, 8, 13, which is the answer.
A, B, C = my_sum(A, B, C)
B, C, A = my_sum(B, C, A)
C, A, B = my_sum(C, A, B)
A, B, C = my_sum(A, B, C)
B, C, A = my_sum(B, C, A)
How would you implement it or what are the other ways of writing this algorithm?
The thing is I can't wrap my head around why this works at all! It was just a random guess that happened to solve my problem.
python don't have pass by reference option, only pass by value, so your construction is correct, because you returning NEW values (in tuple form), not changing value of variables, that you are passing in.
In Python, an assignment made to a parameter name never affects the value of the name that the caller uses. They are separate names that initially reference the same object, but once the parameter name is assigned something else (like a sum), it references a different object.
Your second attempt works because the function returns a tuple with the values of the three paramater names and your main program unpacks that tuple back into its own names.
However, since the function doesn't need the original value of the third argument, and it doesn't touch the first two arguments, the caller doesn't really need to pass the third argument, and doesn't need to update its own names for the first two arguments... So the function could be designed to only take two arguments and return the new value:
def my_sum(x, y):
return x + y
A = 1
B = 2
C = my_sum(A, B)
A = my_sum(B, C)
B = my_sum(C, A)
C = my_sum(A, B)
A = my_sum(B, C)
Lets start with your function definition and one call.
def my_sum(x, y, z):
z = x + y
return x, y, z
A = 1
B = 2
C = 0
my_sum(A, B, C)
Without the function, this is functionally the same as:
A = 1
B = 2
C = 0
x = A
y = B
z = C
z = x + y
_ = x, y, z
# x, y, and z are discarded since you don't do anything with the return value
You shouldn't expect this to change A, B, or C or if you do you have a misconception about how python variables or names work.
Python variables or names are just a dict with a name pointing to a value.
A = 1
B = 2
C = 0
my_sum(A, B, C)
# this is a very condensed version of what python does in the background
dict_of_globals = dict()
dict_of_globals['A'] = 1
dict_of_globals['B'] = 2
dict_of_globals['C'] = 3
my_sum_local_dict = dict()
my_sum_local_dict['x'] = dict_of_globals['A']
my_sum_local_dict['y'] = dict_of_globals['B']
my_sum_local_dict['z'] = dict_of_globals['C']
# and so on..
Since you only ever assign 1 to dict_of_globals['A'], it would be unreasonable to expect it to be anything other than 1.
The reason this works:
A, B, C = my_sum(A, B, C)
is because you are assigning the return value back to A.
A = x # etc..
# or:
dict_of_globals['A'] = my_sum_local_dict['x']

What's the Pythonic way of flipping between two values?

EDIT: My original question was a bit unclear, so I have updated it. Sorry for the inconvenience.
Suppose I have some different values: A, B, C, D, ..., and some variable x that is equal to one of these. Furthermore suppose I know that the opposite of A is B, the opposite of C is D, and so on.
What I want is some clean way of mirroring the boolean x = not x but for these generic values. Right now all I can think of is a horrible if-else chain:
if x == A:
x = B
elif x == B:
x = A
elif x == C:
x = D
elif x == D:
x = C
elif ...
To give some indication of what I'm after, in Haskell I could define this as as:
foo :: x -> x
foo A = B
foo B = A
foo C = D
foo D = C
...
and then simply call foo x.
Build a dict with 2-way mappings. Something like
pairs = [(A, B), (C, D), ...]
d = dict([*pairs, *map(reversed, pairs)])
Then x = d[x].
If for whatever reason you don't like the idea of instantiating a full list of the pairs and reversed pairs, you can use itertools.chain:
d = dict(chain(pairs, map(reversed, pairs)))
One way to do it is
x = B if x == A else A
After editing my question, I came to the realization I can do this:
def flip(x):
if x == A: return B
if x == B: return A
if x == C: return D
if x == D: return C
...
Still, if anyone knows of a better way, feel free to post an answer.

How to define two ways of iterate according input

What is the most pythonic form to define two ways of iterating. In example, I have this original code:
def f1(cat_gen):
for (a, b), c in cat_gen:
if some condition:
yield (a, b), c
but, depending on cat_gen I would need to iterate in this way:
def f1(cat_gen):
for a, b, c in cat_gen:
if some condition:
yield a, b, c
Is there a way to change conditionally just (a, b), c to a, b, c in the for statement
Pass a function that will evaluate the condition properly:
def f1(cat_gen, pred):
for item in cat_gen:
if pred(item):
yield item
f1(flat, lambda a, b, c: ...)
f1(nested, lambda ab, c: ...)
Or, flatten the nested tuples before passing the iterable to f1:
def f1(cat_gen):
for a, b, c in cat_gen:
if ...:
yield a, b, c
f1(map(lambda ab, c: (ab[0], ab[1], c), nested))
You could define it like this
def f1(cat_gen):
# Figure out how to iterate, store it in condition_to_iterate
for item in cat_gen:
if condition_to_iterate:
(a, b), c = item
else:
a, b, c = item
# Do whatever you need with a, b, c
If you want to keep it as 1 function, with two different return forms (maybe not the cleanest decision but depends on the implementation) you'd want to do something like:
def f1(cat_gen, yield_method = 0):
for a, b, c in cat_gen:
if some condition:
if yield_method:
yield a, b, c
else:
yield (a, b), c
and just have the user be aware of the modes of return by that second argument.

Most pythonic way if all in iterable if at least one in iterable

Say I want to see if some objects (a,b,c) in an iterable are in object D. If none are in D, pass. If at least one is in D, all a b and c must be in D, or else an error is raised.
if any(i in D for i in (a,b,c)):
if all(i in D for i in (a,b,c)):
# do something
else:
raise Exception
Is there a more pythonic/less confusing way of doing this?
I would do the "x in D" test only once for each element:
ins = [i in D for i in (a, b, c)]
if any(ins):
if all(ins):
# do something
else:
raise Exception
And borrowing phihag's observation about the implication between all and any:
ins = [i in D for i in (a, b, c)]
if all(ins):
# do something
elif any(ins):
# raise error
If a, b, and c are hashable, you can use sets to express this with helpfully-named methods:
elements = {a, b, c}
if not elements.isdisjoint(D):
if elements.issubset(D):
do_whatever():
else:
raise Whatever
A compact way to express the condition could be:
S = {a, b, c}
if set(D) & S not in (set(), S):
raise Exception()
Your Python code is already an almost verbatim copy of your logic, so I doubt you can simplify the tests.
Since all(X) implies any(X) for non-empty X, you can, however, reorder them so that they are not nested:
props = (a, b, c)
if all(i in D for i in props):
# do something
elif any(i in D for i in props):
raise Exception('Either all or none')
Note that I also pulled out (a, b, c) into a helper variable, which may or may not improve clarity, depending on the complexity.

How does this implementation of swap works in Python?

def fibonacci(num):
a=0
b=1
for i in range(num):
a, b=b, a+b
print a
How does the line inside the loop works?
Somehow a & b 's values change, can seems to understand how..
EDIT:
For some reason I got confused, thought that the middle exp of b=b is something new...
didn't read it well..
It really is (a,b) = (b, a+b) which is the basic form of swap in python (:
b, a+b creates a tuple
This tuple is unpacked back into a and b
This line a, b = b, a+b is equivalent to (a, b) = (b, a+b), which is a tuple assignment.
The line in question can be more clearly written (through tuple packing on the right side and sequence unpacking on the left side) as:
(a, b) = (b, a + b)
As the assignments to a and b are carried out in parallel, this is exactly the same as:
new_a = b
new_b = a + b
a = new_a
b = new_b

Categories