Reputation: 29116
Intuitively a List
class should implement an attribute or a method to retrieve the length of the instance. Fortunately Python'lists have a hidden method called __len__
. Unfortunately this method is not meant to be used directly. I should instead use an external function that will read the hidden method for me.
It is like I need to ask someone else to open the fridge to grab a beer for me. The beer is in the fridge, I have ma both hands and I should be able to do it myself.
Conceptually this approach seems curious. Why not having an attribute, (rather than a method) for getting the length of a list.
In other words, I would prefer using foo.len
instead of foo.len()
or foo.__len__
. len(foo)
appears more bizarre to me.
Is there an explanation for this implementation?
This answer answers partially my question, but my frustration remains.
Upvotes: 3
Views: 450
Reputation: 4186
You can find a deep rationale here and Guido's thoughts here.
For a summary, it’s because they might not be as closely related as you might think. Just talking about the len
vs. __len__
of your post but you can find other examples in the first link.
Let's start by focusing on __len__
:
class Test1:
pass
class Test2:
def __bool__(self):
return False
class Test3:
def __len__(self):
return 0
t1 = Test1()
t2 = Test2()
t3 = Test3()
Now what is the evaluation of t1
, t2
¹, and t3
in a boolean context?
bool(t1)
is True
. Standard python behaviour, anything that is not explicitly False
is considered True
.bool(t2)
is False
. Explicitly setting an object to False
behaves accordingly.bool(t3)
is False
. Since t3
implements __len__
is considered to be a container and since its length is 0
then it’s an empty one. By definition, an empty container is considered False
in a boolean context.__len__
is not bound to be called only by len
.
len
, on the other hand, offers you guaranties:
it will count the number of elements in that container. Whatever it means is dependent of the container though: compare
s = "A string with 𐐐"
d = s.encode("utf-8")
print(len(s)) # outputs 15
print(len(d)) # outputs 18
because s
is a container of characters and d
is a container of bytes.
¹ Note that __bool__
was __nonzero__
in python2.
Upvotes: 5