I promise I've searched around for this, but I haven't found a satisfactory answer (or at least not one that I can understand -- I'm very new to programming).
First, I don't get why the sorting methods in Java are static and are called from the Arrays or Collections classes. Based on my limited understanding of object-oriented programming, to me it would make most sense for those to be called using dot notation on whatever's being sorted. For example, to have something like in Python -- .sort().
BUT, this brings me to my second question, which is why Python implements .sort() as a method and sorted() as a function. I used to think that it's because .sort() is destructive, whereas sorted() returns a new sorted list. But again based on my limited understanding of OOP, it seems like that distinction really isn't material, because methods certainly don't have to be destructive. You could very well implement a method that returns a sorted list and doesn't modify the original.
And if you can provide any insight into static methods in general, that would be great. They still don't make complete sense to me. Well at first they make the most sense -- just call an essentially global function to get what you need. But then when you learn about OOP and about classes specifically being blueprints for objects, then they start to make the least sense. I know the general explanation -- that they're methods that shouldn't depend on any instantiation. But that still doesn't make a whole lot of sense. Especially since we're using them on actual instances of things! Thanks a lot for any help.
Every language has inconsistencies. Some things as functions you think should be methods, some methods that seem like they should be functions, some functions that really "should be objects," et cetera.
Take Python. Though a clean, relatively fresh language with carefully stated, canonical rules about naming and a top-to-bottom rethink (Python 3) relatively recently accomplished, it remains highly inconsistent in its naming. Want a
dict that's ordered?
OrderedDict. One with a default value constructor?
defaultdict. You need a
Queue, but if you access both ends, it's a
deque. I could go on about the naming of modules, parameter passing conventions, module organization, package build techniques, documentation styles, ...
Long story short: If Python, with its intensive focus on readability and common idioms, its recent reorganization, and its opinionated BDFL cannot establish full consistency, as a practical matter, no language of any great breadth or installed base can do much better.
So part of the answer has to be "The world is inconsistent. Even the carefully architected, meticulously curated parts of it. Deal with it."
Regarding the difference between Python's
.sort() methods and its
sorted() function, you are exactly right.
.sort() does an in-place sort of a given collection. It is "destructive," at least of the original ordering (a.k.a. "non-idempotent").
sorted() returns what is effectively a shallow copy of the collection in the desired order.
sorted() also returns sorted collections from iterators and generators--i.e. things that cannot be sorted in-place.
As to why one is a method and the other a function? Design decision. Python's designer thought of functions as operators. While there are a few standard functions that modify objects (e.g.
delattr), most of them are informational or generative, without effect on existing objects. An in-place sort, on the other hand, clearly and utterly depends on the object it's modifying. It has a closer connection to that object, and therefore makes more sense as a method of it.
This isn't set in stone. In Python, you're free to add
.sorted() methods or
.sorted properties to your classes. You can add
.length() methods or
.length properties to your classes, if you wish. But the idiom is to use the standard idioms: the
len() operator, and define the
.__len__() special method if you need to change a class's definition of length. Following the idiom is like following an artistic principle--not strictly required, but useful in creating a consistent body of work. (See also this discussion of Python and the Principle of Least Astonishment.)
Finally, as to static methods, which are also about consistency. OOP languages like Java and Python encourage a lot of code to fall within classes. Some methods don't really depend on object instance values. They may be class operations, effecting all instances of a class; others really are functions. You can keep these as methods, simply ignoring instance values. A lot of code does this, but it's not terribly elegant, and program linters tend to warn "variable
self never referenced!" Specifying that they are
@staticmethod makes their role explicit. It simplifies the code, and it gives a natural home for these "not quite methods that nonetheless live cheek by jowl in the object system."
Why the designers decided on a more static or class-relative invocation (e.g.
Array.sort(a) rather than
lxml.etree.tostring(elt) rather than
elt.tostring()) isn't always entirely clear. I assume the architect thought of this as a generalized operation of the module, rather than an instance method--similar to the way Python's designer viewed