3 ms·
Yeah, the rationale behind many bizarre decisions always seemed to be “because this is the obvious right way”. Or “because Python favors clarity over magic”. An
by skywhopper 4y ago
Yeah, the rationale behind many bizarre decisions always seemed to be “because this is the obvious right way”. Or “because Python favors clarity over magic”. And I’m just “how does that explain why len() is a root namespace method that takes a string arg, while isupper() is a method on string objects?”
- im3w1l 4y agoisupper makes sense only for strings, thus it makes sense not to put it in the global namespace. len is available for strings, but also lists, dicts and many other things. It's so useful that a 3-letter name in the global namespace is justified. Is there a reason for not making it a method though? Yes, actually. By using the dunder system, it makes len special. And it avoids a situation where some objects use a.len() others use b.length() and yet others use c.size() or d.len, e.length, f.size. Python has officially blessed the len name over the others, and that's useful.
- z9znz 4y agoThat defense doesn't really hold up. 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 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.
- bb88 4y agoThis isn't magic at all. 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.
- z9znz 4y agoAhh dunders, and the lack of proper privacy mechanisms... 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.
- bb88 4y agoHumans might care whether or not you called __len__() vs len(), but that's their problem. Guido and the python community didn't enforce this in python, because I think they liked freedom over opinion of what should be done. 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.
- fragmede 4y agobehind closed doors managers call that inability a career limiting move
- z9znz 4y agolen() is not the same as __len__. The built in len() function does type checking and produces a useful exception/message if you provide an argument for which len() is inappropriate. Instead, if you do someobj.__len__ and that object does not implement __len__, then you'll get an exception complaining that he object has no __len__. 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 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).
- saila 4y agoI've been working on a programming language / interpreter, and I'd guess that in the beginning it was probably just easier to implement len() and other such common / generic functions this way. 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).