Armin Ronacher pointed out that one downside of the removal of implicit cross-type comparisons in Python 3 is that it makes it harder to produce a stable repr for mixed-type containers.
This seems like a valid point to me, so I propose adding a suitable "MixedTypeKey" definition to reprlib. Passing "key=reprlib.MixedTypeKey" would then reproduce the old Python 2. sorting behaviour.
We can actually improve on the Python 2 approach by basing the fallback comparison on fully qualified type names rather than on the type id.
Proposed implementation (see https://gist.github.com/ncoghlan/5743523):
>>> class MixedTypeKey:
... """Sort key for mixed type lists in Python 3
...
... >>> sorted([None, ..., 1, 1j, "", (), {}, []])
... Traceback (most recent call last):
... File "<stdin>", line 1, in <module>
... TypeError: unorderable types: ellipsis() < NoneType()
... >>> sorted([None, ..., 1, 1j, "", (), {}, []], key=MixedTypeKey)
... [None, Ellipsis, 1, 1j, '', (), {}, []]
... """
... def __init__(self, k):
... self._sort_key = k
... self._type_name = self._get_fully_qualified_name(k)
... def _get_fully_qualified_name(self, k):
... k_type = type(k)
... # Use __qualname__ if available, __name__ otherwise
... try:
... k_name = k_type.__qualname__
... except AttributeError:
... k_name = k_type.__name__
... return k_type.__module__ + "." + k_name
... def __lt__(self, other):
... # Try standard sorting first
... sort_key = self._sort_key
... try:
... other_sort_key = other._sort_key
... except AttributeError:
... other_sort_key = other
... try:
... return sort_key < other_sort_key
... except TypeError:
... pass
... # If that fails, sort by the fully qualified type names
... try:
... other_type_name = other._type_name
... except AttributeError:
... other_type_name = self._get_fully_qualified_name(other)
... return self._type_name < other_type_name
...
>>> sorted([None, ..., 1, 1j, "", (), {}, []])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: ellipsis() < NoneType()
>>> sorted([None, ..., 1, 1j, "", (), {}, []], key=MixedTypeKey)
[None, 1j, {}, Ellipsis, 1, [], '', ()]
>>> MixedTypeKey(None) < ...
True
The stdlib version could omit the fallback to __name__ (since it doesn't need to cope with old versions of Python)
Support for other comparisons could theoretically be added, but I advise against it without a solid use case (sorting only needs __lt__).
|