Python Forum
Combine Or and And in If Statement
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Combine Or and And in If Statement
#1
I am trying to write some code to return True if either AH, MH or GL are in mStrip. I want to apply a condition to GL (but not AH or MH) so that it is True only if ML is not in mStrip.

Is the code below correct? Is there a better way of writing it? I want to add additional And/Or conditions in the future and I can see it getting confusing very quickly.

if 'AH' in mStrip or 'MH' in mStrip or 'GL' in mStrip and not 'ML' in mStrip:
Reply
#2
It's valid Python, so it might be correct. You can verify if the code is correct by trying some test cases:
def check(mStrip):
    result =  'AH' in mStrip or 'MH' in mStrip or 'GL' in mStrip and not 'ML' in mStrip
    print(mStrip, 'is',  result)

check('AH')
check('ML')
check('AH ML')
check('GL ML')
check('XL'))
Output:
AH is True ML is False AH ML is True GL ML is False XL is False
The interesting results are that check('AH ML') is True but check('GL ML') is FALSE. I don't think that is what you want.

From the manuals: https://docs.python.org/3/reference/expr...tion-order
Quote: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.

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

In the case of 'AH ML' we start with 'AH' in mStrip which is True. This is the "x" value from the description above. If x is true, it's value is returned.

The 'GL ML' case is a bit more interesting. If we evaluate each individual condition and replace them with True or False we get: False or False or True and False. Breaking this into "x" and "y" we get:
x = False or y = False or True and False

x is False, so we don't return immediately and must evaluate Y. Y is a compound statement so we break it into x and y again: x = False or y = True and False.

Once again x is False, so we have to evaluate y. x = True and y = False.

This time we have "and" instead of "or". If x is false, "and" returns false, else it evaluates y and returns y. In our case x is True, so we have to evaluate y and return that. y is False, so the "and" statement returns False.

Stringing a bunch of "and"s and "or"s is complicated and best avoided. It is also lengthy to type and difficult to read.
def check(mStrip):
    result = 'ML' not in mStrip and any(x in mStrip for x in ['AH', 'MH', 'GL'])
    print(mStrip, 'is',  result)

check('AH')
check('ML')
check('AH ML')
check('GL ML')
check('XL'))
Output:
AH is True ML is False AH ML is False GL ML is False XL is False
I think this code produces the results you want. To be True mStrip cannot contain ML and it must contain AH or MH or GL.

I could have used "not 'ML' in mStrip", but I think " 'ML' not in mStrip" reads more naturally.

This is a "comprehension":
any(x in mStrip for x in ['AH', 'MH', 'GL'])
This part:
x in mStrip for x in ['AH', 'MH', 'GL']
Creates a list that is essentially ['AH' in mStrip, 'MH' in mStrip, 'GL' in mStrip]. The any() function returns True if any of the conditions is True.
JamesA likes this post
Reply
#3
Parenthesis can help break this into segments for you. I think this is what you want
if ('AH' in mStrip or 'MH' in mStrip) or ('GL' in mStrip and not 'ML' in mStrip):
JamesA likes this post
Reply
#4
(Jul-22-2021, 06:50 PM)jefsummers Wrote: Parenthesis can help break this into segments for you. I think this is what you want
if ('AH' in mStrip or 'MH' in mStrip) or ('GL' in mStrip and not 'ML' in mStrip):
I was thinking this instead:
if ('AH' in mStrip or 'MH' in mStrip or 'GL' in mStrip) and not 'ML' in mStrip:
But that just shows that a string of and and or statements is bad code.
Reply
#5
Agree. Almost always ambiguous.
Reply
#6
I'm just an amateur, but I thought this might help on a different track:

import random # just for making the strings

# list of things we want, add what you like or generate it 
wanted = ['AH', 'MH', 'GL', 'ML']
# list of things we need to check, add what you like or generate it 
check_list = ['GL']
# list of things we do not want in combination with check_list, add what you like or generate it 
not_wanted_in_comboGL = ['ML']
# if for example you also don't want AH in combo with say MH
# not_wanted_in_comboAH = ['MH']
# you need another if in lookup(alist)

# make a random test set as a substitute for mStrip

def makeTestset():
    test_set = []
    while len(test_set) < 10:
        randomUpperLetter1 = chr(random.randint(ord('A'), ord('M')))
        randomUpperLetter2 = chr(random.randint(ord('A'), ord('M')))
        string = randomUpperLetter1 + randomUpperLetter2
        if string in wanted: 
            test_set.append(string)
            print(string)
    return test_set

def lookup(mylist):
    # maybe there are no unwanted combinations
    for x in mylist:
        if x in wanted:            
            result = 'OK'
            print(x, result)        
        if x in check_list:
            # check if an unwanted combination is there
            for y in mylist:
                if y in not_wanted_in_comboGL:
                    result = 'not OK'
                    print(x, y, result)
                    # break if an unwanted combination is found
                    return result
    return result
    
alist = makeTestset()
answer = lookup(alist)
print(answer)
JamesA likes this post
Reply
#7
Thanks for the responses guys. They've really helped and I've learned a lot.
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020