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?