0

I am trying to understand how python keywords and operators interact with object dunder methods and have encounter a situation I don't quite understand.

Setup: I created two simple classes with the __bool__ dunder method that I believe should be used when checking for truthy conditions. I have them always return true which should be the same behavior as if the method was not defined on the class, but I have added a print statement so I can see when the method is called.

class ClassA(object):

    def __bool__(self):
        print('__bool__ check classA')
        return True

class ClassB(object):

    def __bool__(self):
        print('__bool__ check classB')
        return True

Test 1:

if a and b: print(True)

The output is what I expected to see:

__bool__ check classA
__bool__ check classB
True

Test 2:

c = (a and b)
print(c)

The output is NOT what I expected to see:

__bool__ check classA
<__main__.ClassB object at 0x0000020ECC6E7F10>

My best guess is that Python is evaluating the logic from left to right, but doesn't call the __bool__ method on the final object until needed. (I don't understand why, but I think that is what is happening)

Test 3:

c = (a and b and a)
print(c)

This output agrees with my assumption.

__bool__ check classA
__bool__ check classB
<__main__.ClassA object at 0x0000026A81FA7FD0>

Test 4:

c = (a and b and a)
if c: print(True)

Further calling if c then evaluates the check on the object.

__bool__ check classA
__bool__ check classB
__bool__ check classA
True

Question Why doesn't an expression such as (True and a) fully evaluate inside the parenthesis?

gomory-chvatal
  • 332
  • 1
  • 3
  • 10

1 Answers1

3

The expression a and b returns the left hand operand if a is falsy and returns the right hand operand if a is truthy. There is no need to evaluate the boolean value of b.

However, when used in an if statement, it is necessary to check if the entire expression gives a truthy value.

Unmitigated
  • 76,500
  • 11
  • 62
  • 80