Problem with the "and" operator in python

Viewed 53

I was working on what seemed an easy exercise but I ran into a problem with a the "and" orperator. I think I understand quite well how it works:

print("0 and 0 : ", (0 and 0))
print("0 and 1 : ", (0 and 1))
print("1 and 0 : ", (1 and 0))
print("1 and 1 : ", (1 and 1))
print((0 and 1) == (1 and 0))

That gives me the expected results :

0 and 0 :  0
0 and 1 :  0
1 and 0 :  0
1 and 1 :  1
True

BUT when I run this code:

A = '00111000111000111000111000'
B = '00001110001110001110001110'

for i in range(len(A)):
    s1s2 = (A[i] and B[i])
    s2s1 = (B[i] and A[i])
    print(f"s1={A[i]}, s2={B[i]}, s1 and s2 = {s1s2} ; s2 and s1 = {s2s1} :", (s1s2) == (s2s1))

I end up with non-sense results in my console using Python3.9 on VSCode(and I tried online too):

s1=1, s2=0, s1 and s2 = 0 ; s2 and s1 = 1 : False
s1=0, s2=1, s1 and s2 = 1 ; s2 and s1 = 0 : False

The same problem goes with "or".

A = '00111000111000111000111000'
B = '00001110001110001110001110'
for i in range(len(A)):
    s1s2 = (A[i] or B[i])
    s2s1 = (B[i] or A[i])
    print(f"s1={A[i]}, s2={B[i]} || s1 or s2 = {s1s2} ; s2 or s1 = {s2s1}  || s1s2 == s2s1 : ", (s1s2) == (s2s1))

That returns non-sense things :

s1=1, s2=0 || s1 or s2 = 1 ; s2 or s1 = 0  || s1s2 == s2s1 :  False
s1=0, s2=1 || s1 or s2 = 0 ; s2 or s1 = 1  || s1s2 == s2s1 :  False

I've tried to transform the string A and B into lists before running the loop but it gave me the same results. Do you have any idea about what's going on here? What I am missing or doing wrong ?

3 Answers

and returns first value that evaluates to False or last value in case none of them are false. Boolean value of strings in Python is True for each non-empty ("") string. Which means even '0' is considered True while 0 (integer) is not.

Taking both into account, when you do s1 and s2 char by char, its value will be s2, because it is the last one and all evaluated to True.

Let's take a slightly different case:

0 and None -> 0
None and 0 -> None
'1' and '0' -> '0'
'0' and '1' -> '1'

Why? Well in the first two cases, both 0 (an integer) and None are falsy, so the first one is returned. In the second two cases, you have non-empty strings, both of which are truthy, so the second one is returned.

There are a couple of ways to fix the second result to be there same as integer comparison. The simplest way is to convert each bit to integer on demand:

A = '00111000111000111000111000'
B = '00001110001110001110001110'
for a, b in zip(A, B):
    a = int(a)
    b = int(b)
    s1s2 = (a and b)
    s2s1 = (b and a)
    print(f"s1={a}, s2={b}, s1 and s2 = {s1s2} ; s2 and s1 = {s2s1} :", (s1s2) == (s2s1))

Another way is to work with integers directly and use bit-twiddling. This requires you to set the number of bits explicitly, since something like int(A).bit_length() will discard any leading zeroes:

N = 26
A = 0b00111000111000111000111000
B = 0b00001110001110001110001110
for i in range(N - 1, -1, -1):
    a = (A & (1 << i)) >> i
    b = (B & (1 << i)) >> i
    s1s2 = (a and b)
    s2s1 = (b and a)
    print(f"s1={a}, s2={b}, s1 and s2 = {s1s2} ; s2 and s1 = {s2s1} :", (s1s2) == (s2s1))

You can omit >> i in both cases since it does not affect the truth value of the bit selected by (X & (1 << i)). Also, 1 << i == 2**i.

You're doing AND or OR on non-empty strings. Those always evaluate to True.

Related