0

When I try this code:

a = ['b']

if 'i' or 'j' in a:
    print('Yes!')
else:
    print('No!')

The output is 'Yes!'. Flake8 and python do not complain about an error or bad form.

What exactly happens when the code runs? Why does 'i' or 'j' in a evaluate as true?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
  • `'i' or 'j'` is evaluated too true so `Yes` is shown. Looking for a duplicate to mark. – 0stone0 Jan 20 '22 at 13:05
  • 1
    @0stone0 No, `i` evaluated to `True`; the other expression `'j' in a` isn't evaluated at all. `in` has higher precedence than `or` – chepner Jan 20 '22 at 13:18
  • If `'i' or 'j'` evaluated to true, then `True in a` would have been false and `No!` would have been printed. – chepner Jan 20 '22 at 13:29

1 Answers1

0

The problem is that in the statement i evaluates to True, which means that the statement is then: True or 'j' in a. This is always true and your result will be always 'Yes!'.

You can use something like this to check if one of the values is in your list:

a = ['b']
chars_to_check = ['i', 'j']

filtered_list = [i for i in a if i in chars_to_check]

if len(filtered_list)>0:
    print('Yes!')
else:
    print('No!')

The example is from this question, where people also posted more efficient or shorter solutions to your problem. The solution I like the most would be this one:

a = ['b']
if {'i','j'} & set(a):
    print('Yes!')
else:
    print('No!')

EDIT: I think now I understand the question.

First of all python sees that you have a or in your if statement. This means the first expression (in your case 'i') is evaluated first. If the first expression is True, the whole statement is True and the second expression is not even evaluated. The evaluation order of or is explained here for example.

Now to why the first expression is always True. Python automatically evaluates all objects not only boolean values. For this the objects can for example contain a function __bool__() that gives back the boolean value of an object. The object in your case is a single character 'i', which evaluates to True. The reason is that it is defined that the boolean value of a string is always True except for the empty string (''). Here you can see an example of the evaluation:

print(bool('i')) # True
print(bool('')) # False

An answer that shows which objects are considered False and which are considered True can you find here.

JANO
  • 2,995
  • 2
  • 14
  • 29
  • Many thanks! Your answers clarified the path for further analysis :) – Nils Galindo-Sjöberg Jan 20 '22 at 14:01
  • No problem. Just looked at it again and the correct solution for your particular case is to use `if 'i' in a or 'j' in a:`. The other things are only more complicated. The thing to remember is that the `or` divides the statement into two and evaluates the sub-statements. In your case, the first sub-statement made no sense and therefore the `if` evaluated always to `True`. – JANO Jan 20 '22 at 14:21
  • Thanks JANO, my real question is how come that the if-statement gets at True-value in this particular setting. That is, how does Python handle it, syntactically and in execution? – Nils Galindo-Sjöberg Jan 20 '22 at 15:19
  • Ok, I think I got it now. Please see my edit. – JANO Jan 20 '22 at 15:40
  • So, as I understand it at the moment from your and chepners' comment: " 'j' in a" is treated as a unit which makes Python choose a boolean value for the resting part of the disjunction? – Nils Galindo-Sjöberg Jan 20 '22 at 16:04
  • Yes " 'j' in a " is one unit and the other unit is only " 'i' ". For better understanding, you can also use parentheses, which is valid Python: `if (('i) or ('j' in a)):`. In order to evaluate the `or` the two units have to be evaluated. Python takes a shortcut here and only evaluates the first unit as it then already knows that one of both sides is `True`. – JANO Jan 20 '22 at 19:06