> modern (to me) Ruby's lambda syntax (in the .map call)
It's syntactic sugar for what Ruby does with a lambda, but fundamentally the purpose is to extract a method from the input. Python has that in the standard library, as `operator.attrgetter`. But also in Python, you generally convert by passing to the type constructor rather than calling a method; so you can just use that directly.
> parentheses-less function calls
Only methods are called here, not plain functions. You can get this effect in many other languages by defining properties instead of zero-argument methods.
> the fact that arrays implement <=> by comparing each item in order
This is also done in Python, and probably many other languages.
> that there's an overloadable compare operator at all, having multiple value assignments in one go
Idem.
> In any other language I can think of real quick (TS, Elixir, C#, Python, PHP, Go) a fair number of these parts would be substantially more wordy or syntaxy at little immediately obvious benefit.
A relatively direct translation looks like:
import functools
@functools.total_ordering
class AppVersion:
def __init__(self, version_string):
self.major, self.minor, self.patch, *_ = map(int, str(version_string).split('.'))
def __lt__(self, other):
return (self.major, self.minor, self.patch) < (other.major, other.minor, other.patch)
def __eq__(self, other):
return (self.major, self.minor, self.patch) == (other.major, other.minor, other.patch)
def __str__(self):
return f'{self.major}.{self.minor}.{self.patch}'
You don't need any `end`s, but you don't (in 3.x) have the convenience of a direct `<=>` analog (it used to be `__cmp__`). The actual integer conversion function could be done differently, of course, to handle invalid values (I don't know why the Ruby code is doing the `|| 0` business; `to_i` already takes care of that AFAICT).
Although the rough ecosystem equivalent of Gem::Version (https://github.com/pypa/packaging/blob/main/src/packaging/ve...) does much more sophisticated parsing. And you could also get the comparison logic by leveraging `collections.namedtuple`, `typing.NamedTuple` (but changing the initialization logic isn't so neat for these immutable types), `dataclasses.dataclass` etc. as in js2's reply.