Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When will the Python special method (i.e. magic method) __len__ be called?

I'm trying to make a Link-List Node class for a Link-List, as shown in the code:

class LNode:
    def __init__(self, data=None, pnext=None):
        self.data = data
        self.pnext = pnext

    def __str__(self):
        return f"{self.data} -> {self.pnext}"

    def __repr__(self):
        return f"{self.data} -> {self.pnext}"

    def __len__(self):
        print("len method called...")
        cnt = 1
        return cnt

    def headInsert(self, nextNode):
        nextNode.pnext = self.pnext
        self.pnext = nextNode

    def headInsert_woh(self, nextNode):
        nextNode.pnext = self
        return nextNode

    def tailInsert(self, nextNode):
        print("tail-insert called...")
        tail = self
        while tail.pnext:
            tail = tail.pnext
        tail.pnext = nextNode
        return self

    def __add__(self, other):
        return self.tailInsert(other)

After definition, I tried codes below:

a = LNode(1)
for i in range(2, 6):
    a += LNode(i)
print(a)

Strangely, __len__ method will be called repeatedly and recursively when the tail-insert method is called, or when the node pointer of the Link-List node moves. As shown below:

tail-insert called......
tail-insert called......
len method called......
tail-insert called......
len method called......
len method called......
tail-insert called......
len method called......
len method called......
len method called......
1 -> 2 -> 3 -> 4 -> 5 -> None

But why? I thought __len__ is the implementation of BIF len(), why it will be called here? Thanks a lot.

like image 492
Thijs_Bitkid Avatar asked Sep 06 '25 03:09

Thijs_Bitkid


2 Answers

This is because you're testing the truth value of tail.pnext as a while condition in this line:

while tail.pnext:

According to Python's documentation of Truth Value Testing:

By default, an object is considered true unless its class defines either a __bool__() method that returns False or a __len__() method that returns zero, when called with the object.

Since your LNode class does not have a __bool__ method defined, the __len__ method is called instead for the truth value testing.

like image 145
blhsing Avatar answered Sep 07 '25 21:09

blhsing


The documentation for __len__ (on the Data model page) states:

Also, an object that doesn’t define a __bool__() method and whose __len__() method returns zero is considered to be false in a Boolean context.

Your

while tail.pnext:

does put tail.pnext in a Boolean context and evaluates its truth value. Since your object doesn't have a __bool__ method, Python tries __len__ instead.

If you add a __bool__ method that also prints such a message, you'll see that that gets called instead of __len__ (for the code you showed - of course it would be called when using len).

like image 36
Kelly Bundy Avatar answered Sep 07 '25 20:09

Kelly Bundy