You can either implement a single function that handles the various types or you can add a method to each type, but the latter can be pretty tedious when you're implementing a bunch of functions / methods that operate on numerous types.
I ran into this when I started implementing map() for a few builtin types and found myself duplicating the loop-over-items code and started thinking, "Hmm, I wonder if this is why Python implemented len(), map(), etc as functions instead of methods."
Of course, you could implement an abstract class or equivalent for the generic aspects of len(), map(), etc, but then that gets pretty complex too.
So, based on that, which to reiterate is just a guess, I don't think the decision is bizarre at all. It's just an implementation tradeoff.
From a developer point of view, I slightly prefer method syntax for aesthetic reasons, but practically speaking it doesn't make any difference whether I type obj.len() or len(obj), and IMO neither is more or less object-oriented than the other (which is a common complaint about len() et al).
There is a method on all objects which are a collection:
__len__()
You can try it out yourself. x = [1,2,3,4]
print(x.__len__()) #4
y = "abcdefg"
print(y.__len__()) #7
z = { "a": "b", "c": "d"}
print(z.__len__()) #2
len() is just syntactic sugar for calling __len__(). The "dunder" methods in python are a standard way of extending classes.It is generally considered bad practice to call dunders, with the idea being that those are more private or more internal methods which you should not directly depend on (unless you're willing to accept more breaking changes). Instead, you depend on other more intentionally public methods which themselves may call dunders or do other things.
I think the issue the parent was really focusing on was the fact that many methods are instance methods, but there are some global functions.
Python suffers from being sort of OO, sort of procedural, and even less but also sort of functional.
It's a bit like a flying camper van. It's a weird plane with tricky controls, poor fuel economy, cramping sleeping areas, and a bump ride on roads. But boy does it have a lot of mods and attachments, so you can do just about anything with it using only a pair of plyers and a flathead screwdriver. Thus, it attracts beginner mechanics and enthusiasts who build monstrosities that experienced mechanics have nightmares about but which pointy haired bosses don't understand and think the feature set is great.
It seems like your inability to understand why a lot of people like python seems more like a professional inability (liability?) to adapt and change with the flow.
Likewise, for example, abs() and __abs__() raise different exceptions if used on something for which abs() makes no sense. Like len(), the built in failure exceptions are somewhat more useful than the dunder exceptions.
But even the type related exceptions of the built in functions are inconsistent -- unnecessarily so.
abs("hello") => TypeError: bad operand type for abs(): 'str'
len(5) => TypeError: object of type 'int' has no len()
At least the exception types are uniform: TypeError. But the exception messages should be identical rather than two very different statements which say the same thing: function f is not appropriate for argument type t.
You may call all this inconsistency freedom, but it's just lack of structure and general sloppiness. Or to be kinder, it's just more organic. Inconsistency and organic evolution are not hallmarks of good tools for building reliable systems.
Finally, I do not lack the inability to understand why a lot of people like Python. I just posit that most people who like Python only like it because they don't (yet) know better. http://www.paulgraham.com/avg.html . Python is Blub in this essay.
If you don't know better, Python is a marvelous language. Although I would argue that some of the oddities and awkwardness of Python misshape peoples' thought processes in a way that can be difficult to unlearn (and which I believe even make these conversations like we are having in this topic extra challenging).
Back to python. There is no type checking in len() or abs(). The best argument I can make is through code. You, yourself, can make your own len() method. This will behave exactly like len(). Let's call it len2():
def len2(obj):
try:
return obj.__len__()
except AttributeError:
raise TypeError(f"object of type '{type(obj)}' has no len()")
There's no type checking, because there's no need. All that object needs is for __len__() to exist. Further we can add abs() and len() to an object like this: class X:
def __len__(self):
return 2
def __abs__(self):
return 1
assert len(X()) == 2
assert len2(X()) == 2
assert abs(X()) == 1
Fortunately you can fix the issues with error uniformity by creating your own functions if you choose the method above. I won't judge you -- because there might be a reason you may want that.> I just posit that most people who like Python only like it because they don't (yet) know better.
I don't know about that. I came from a background of C/C++/Ada/Java/68k Assembly/Perl/Shell Script/Awk/etc. I switched to python about a decade ago. The python community has some of the smartest people I've ever met. To me the smart people seem to have gravitated towards python.
len() makes no sense for numbers, booleans, None, and probably other types I cannot think of at the moment.
Python has a LONG list of built in functions, most of which are only applicable to a few types of parameters. They could just as well be object methods.
https://docs.python.org/3/library/functions.html
Some of those methods, like sorted(), are terrible names. A past tense verb name... so instead of telling Python, "Hey, please sort() this", you say "Please give me a sorted() thing".
So you have the collection.sort() method or the built in sorted() method. They both sort, but they have different options; and significantly, sort() mutates the collection, whereas sorted() returns a new collection.
This is inconsistent and creates unnecessary cognitive load. It's another example of a Frankenstein language.