'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.
Solution 1:[1]
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.
Solution 2:[2]
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).
Solution 3:[3]
If you change your prints to:
def __len__(self):
print("len method called on", self.data)
# ...
def tailInsert(self, nextNode):
print("tail-insert called on", self.data, "for", nextNode.data)
# ...
After running your code, you'll get:
tail-insert called on 1 for 2
tail-insert called on 1 for 3
len method called on 2
tail-insert called on 1 for 4
len method called on 2
len method called on 3
tail-insert called on 1 for 5
len method called on 2
len method called on 3
len method called on 4
1 -> 2 -> 3 -> 4 -> 5 -> None
Which shows that every time you call tailInsert and check tail.pnext if tail.pnext is not None the __len__ is called on it as it's in front of a while.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | |
| Solution 3 | re-za |
