How can I get 2.x-like sorting behaviour in Python 3.x?

后端 未结 10 879
陌清茗
陌清茗 2020-11-27 06:50

I\'m trying to replicate (and if possible improve on) Python 2.x\'s sorting behaviour in 3.x, so that mutually orderable types like int, float etc.

10条回答
  •  时光说笑
    2020-11-27 07:27

    @martijn-pieters I don't know if list in python2 also has a __cmp__ to handle comparing list objects or how it was handled in python2.

    Anyway, in addition to the @martijn-pieters's answer, I used the following list comparator, so at least it doesn't give different sorted output based on different order of elements in the same input set.

    @per_type_cmp(list) def list_cmp(a, b): for a_item, b_item in zip(a, b): if a_item == b_item: continue return python2_sort_key(a_item) < python2_sort_key(b_item) return len(a) < len(b)

    So, joining it with original answer by Martijn:

    from numbers import Number
    
    
    # decorator for type to function mapping special cases
    def per_type_cmp(type_):
        try:
            mapping = per_type_cmp.mapping
        except AttributeError:
            mapping = per_type_cmp.mapping = {}
        def decorator(cmpfunc):
            mapping[type_] = cmpfunc
            return cmpfunc
        return decorator
    
    
    class python2_sort_key(object):
        _unhandled_types = {complex}
    
        def __init__(self, ob):
           self._ob = ob
    
        def __lt__(self, other):
            _unhandled_types = self._unhandled_types
            self, other = self._ob, other._ob  # we don't care about the wrapper
    
            # default_3way_compare is used only if direct comparison failed
            try:
                return self < other
            except TypeError:
                pass
    
            # hooks to implement special casing for types, dict in Py2 has
            # a dedicated __cmp__ method that is gone in Py3 for example.
            for type_, special_cmp in per_type_cmp.mapping.items():
                if isinstance(self, type_) and isinstance(other, type_):
                    return special_cmp(self, other)
    
            # explicitly raise again for types that won't sort in Python 2 either
            if type(self) in _unhandled_types:
                raise TypeError('no ordering relation is defined for {}'.format(
                    type(self).__name__))
            if type(other) in _unhandled_types:
                raise TypeError('no ordering relation is defined for {}'.format(
                    type(other).__name__))
    
            # default_3way_compare from Python 2 as Python code
            # same type but no ordering defined, go by id
            if type(self) is type(other):
                return id(self) < id(other)
    
            # None always comes first
            if self is None:
                return True
            if other is None:
                return False
    
            # Sort by typename, but numbers are sorted before other types
            self_tname = '' if isinstance(self, Number) else type(self).__name__
            other_tname = '' if isinstance(other, Number) else type(other).__name__
    
            if self_tname != other_tname:
                return self_tname < other_tname
    
            # same typename, or both numbers, but different type objects, order
            # by the id of the type object
            return id(type(self)) < id(type(other))
    
    
    @per_type_cmp(dict)
    def dict_cmp(a, b, _s=object()):
        if len(a) != len(b):
            return len(a) < len(b)
        adiff = min((k for k in a if a[k] != b.get(k, _s)), key=python2_sort_key, default=_s)
        if adiff is _s:
            # All keys in a have a matching value in b, so the dicts are equal
            return False
        bdiff = min((k for k in b if b[k] != a.get(k, _s)), key=python2_sort_key)
        if adiff != bdiff:
            return python2_sort_key(adiff) < python2_sort_key(bdiff)
        return python2_sort_key(a[adiff]) < python2_sort_key(b[bdiff])
    
    @per_type_cmp(list)
    def list_cmp(a, b):
        for a_item, b_item in zip(a, b):
            if a_item == b_item:
                continue
            return python2_sort_key(a_item) < python2_sort_key(b_item)
        return len(a) < len(b)
    

    PS: It makes more sense to create it as an comment but I didn't have enough reputation to make a comment. So, I'm creating it as an answer instead.

提交回复
热议问题