Search code examples
pythonoopif-statementbooleanmagic-methods

Does “if” have implicit access to a class’s special methods even when they are not directly called?


I don’t understand how the code in the for loop knows whether the instance of the class is truthy or not. My understanding is probably faulty but what I think is happening is this: the call to bool calls the altered special method bool which has been modified to use the len built in on the instance. But outside of the class, when we get to the for loop and the if conditional I don’t know how “if” knows whether the order is truthy unless it is calling the special method bool(behind the scenes).

I apologise if this is not the forum for such a question but I don’t know enough of the required terminology to find an answer to this on google or a good book to get my head around OOP with a python bias.

Code

class Order:
    def __init__(self, cart, customer):
        self.cart = list(cart)
        self.customer = customer

   # def __bool__(self):
          #print("\'__bool__ got called\'")
          #return len(self.cart) > 0
        
order1 = Order(['banana', 'apple', 'mango'], 'Real Python')
order2 = Order([], 'Python')


for order in [order1, order2]:
    if order:
        print(f"{order.customer}'s order is processing...")
    else:
        print(f"Empty order for customer {order.customer}")


print(bool(order2))

Solution

  • Please refer Python3 docs

    In short, your object's base class has a __bool__ function that can be implemented to define truthfulness. If it's not defined on the type, then the base class implementation will be called. It functions as you said, by calling __len__.

    If neither __bool__ nor __len__ are defined, all the class objects are considered true.

    Edit to my answer with @chepner's contribution from the comments:

    In order to evaluate bool(Order()), Python tries to:

    1. find a definition of Order.__bool__,
    2. then a definition of __bool__ for some parent class.
    3. If it still hasn't found one, it looks for Order.__len__,
    4. then for a parent that defines __len__.
    5. If none of those are found, then bool(Order()) simply returns True.