I intended to show some benefits of not using magic methods directly:
1- Readability:
Using built-in functions like len()
is much more readable than its relevant magic/special method __len__()
. Imagine a source code full of only magic methods instead of built-in function... thousands of underscores...
2- Comparison operators:
class C:
def __lt__(self, other):
print('__lt__ called')
class D:
pass
c = C()
d = D()
d > c
d.__gt__(c)
I haven't implemented __gt__
for neither of those classes, but in d > c
when Python sees that class D
doesn't have __gt__
, it checks to see if class C
implements __lt__
. It does, so we get '__lt__ called'
in output which isn't the case with d.__gt__(c)
.
3- Extra checks:
class C:
def __len__(self):
return 'boo'
obj = C()
print(obj.__len__()) # fine
print(len(obj)) # error
or:
class C:
def __str__(self):
return 10
obj = C()
print(obj.__str__()) # fine
print(str(obj)) # error
As you see, when Python calls that magic methods implicitly, it does some extra checks as well.
4- This is the least important but using let's say len()
on built-in data types such as str
gives a little bit of speed as compared to __len__()
:
from timeit import timeit
string = 'abcdefghijklmn'
print(timeit("len(string)", globals=globals(), number=10_000_000))
print(timeit("string.__len__()", globals=globals(), number=10_000_000))
output:
0.5442426
0.8312854999999999
It's because of the lookup process(__len__
in the namespace), If you create a bound method before timing, it's gonna be faster.
bound_method = string.__len__
print(timeit("bound_method()", globals=globals(), number=10_000_000))
Best Answer
AFAIK,
len
is special in this respect and has historical roots.Here's a quote from the FAQ:
The other "magical methods" (actually called special method in the Python folklore) make lots of sense, and similar functionality exists in other languages. They're mostly used for code that gets called implicitly when special syntax is used.
For example:
and so on...