Get which or-condition is met in an if-statement in Python

Viewed 1577

I have the following Python if-statement:

if element1 in list[2] or element2 in list[2] or 3 in list[2]:
   do some more if-statements...
else:
   print something...

Is it possible to print (or get in some way) which of the or-conditions are met (if there are any)? Because there are other if-statements in the if-clause, I don't want to use three different if-statements, which would be one solution. But this will end up in nearly 12 different if-statement. I just want to log which condition is met for further analysis. I googled of course, but found nothing so for. Thanks in advance.

Edit: Use case: try to implement a search algorithm with "AND" and "OR" function. I'm sure could be written much better and cleaner but for now counts what works ;-)

6 Answers

You can assign the boolean values to names:

a = element1 in list[2]
b = element2 in list[2]
c = 3 in list[2]
if a or b or c:
    print(f"{element1} {'in' if a else 'not in'} list[2]")
    print(f"{element2} {'in' if b else 'not in'} list[2]")
    print(f"3 {'in' if c else 'not in'} list[2]")

This removes any benefits of short-circuiting though, since all the expressions need to be evaluated.

In python 3.8+ you can use assignment expressions:

if (a := (element1 in list[2])) or \
   (b := (element2 in list[2])) or \
   (c := (3 in list[2])):
    try:
        print(f"{element1} {'in' if a else 'not in'} list[2]")
        print(f"{element2} {'in' if b else 'not in'} list[2]")
        print(f"3 {'in' if c else 'not in'} list[2]")
    except NameError:
        pass

Keep in mind that this approach has the opposite problem: some names may not have been assigned because of short circuiting. You won't know the result for any name past the first false one.

Another way, if you really have a lot of conditions, is to build a list instead of naming each individual one:

test = [element1, element2, 3]
x = [t in list[2] for t in test]
if any(x):
    for t, r in zip(test, x):
        print(f"{t} {'in' if r else 'not in'} list[2]")

You can even go so far as to use sets:

test = {element1, element2, 3}
s2 = set(list[2])
x = test & s2
if x:
    print('{x!r} in list[2]')
    if y := test - x:
        print('{y} not in list[2]')

The choice depends on your specific use-case.

If you wanted cleaner code then I would suggest putting the if statement checks inside a function and using a logger to log the truth of the statement.

import logging

def elem_in_list(elem, list):
    if elem in list[2]:
        logging.info(f"{elem} is in list")
        return True
    logging.info(f"{elem} is not in list")
    return False

if elem_in_list(element1, list) or elem_in_list(element2, list) or elem_in_list(element3, list):
    # do more if statements
else:
    print(something)

This is perhaps a cleaner way of doing it, but you cannot get around using a load of if statements for comparisons as far as I know.

I would probably do something like this, if I wanted leverage short-circuiting:

Create a condition variable with a known starting value. Then create an else-if chain with all your conditions, and for each one set a different value for the condition variable if the boolean expression evaluates to True. Then, create an if statement after the else-if block, which checks whether the condition variable's value has changed.

condition = 0
if (expression)==(expression):
    condition = 1
elif (expression)==(expression):
    condition = 2
elif (expression)==(expression):
    condition = 3

if condition:
   # perform common actions here
   if condition == 1:
       print(f"Condition met was condition ONE!")

You could also execute any condition-specific code in the else-if block, instead of executing it in the last if block, if you think that would be more readable in your case.

You could also move the common code that needs to be executed in multiple cases to a function.

if (expression)==(expression):
    # case 1 code here
    common_code()
elif (expression)==(expression):
    # case 2 code here
    common_code()
elif (expression)==(expression):
    # case 3 code here
    # Maybe this one does not need to call common_code()? This solution is fairly flexible.

You can do something like this:

if element1 in list[2] or element2 in list[2] or 3 in list[2]:
    if element1 in list[2]:
        print('element1 found in list[2]')
    if element2 in list[2]:
        print('element1 found in list[2]')
    if element3 in list[2]:
        print('element1 found in list[2]')
    #=== do some more if-statements...
else:
   #== print something...

Inside the grouped if condition you can check which condition was actually fulfilled.
I don't know better way to do this.

I think you could use a Python dictionary for this purpose. As an example:

list1=[list2,list2,list2]
list2=[3,4,6]
list3=[4,6,7]
element1=0
element2=0

if element1 in list1[2] or element2 in list1[2] or 3 in list1[2]:
  dict_conditions={"Condition 1":str(element1 in list1[2]),"Condition 2":str(element2 in list1[2]) ,"Condition 3":str(3 in list1[2])}
  print(dict_conditions)

Which would print the following output:

{'Condition 1': 'False', 'Condition 2': 'False', 'Condition 3': 'True'}

This can be done very simply by making the result of each condition a name/number:

>>> res = ((element1 in lst[2] and 1) or
...        (element2 in lst[2] and 2) or
...        (3 in lst[2] and 3))
>>> if res:
...     print('condition', res, 'succeeded')
...
condition 2 succeeded

If you're using python >= 3.8 this can be wrapped up more neatly, like this:

if (res := (element1 in lst[2] and 1) or
           (element2 in lst[2] and 2) or
           (3 in lst[2] and 3)):
    print('condition', res, 'succeeded')

If none of the conditions is true, res will be False, otherwise it will be the name/number of whichever condition succeeded.


The reason why this works is explained in the python reference for boolean expressions:

The expression x and y first evaluates x; if x is false, its value is returned; otherwise, y is evaluated and the resulting value is returned.

Thus, for the above approach to work properly, the name/number must evaluate to true (so just avoid using empty strings, zero, or anything for which bool(n) is false).

Related