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

后端 未结 10 856
陌清茗
陌清茗 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:21

    We can solve this problem in the following way.

    1. Group by type.
    2. Find which types are comparable by attempting to compare a single representative of each type.
    3. Merge groups of comparable types.
    4. Sort merged groups, if possible.
    5. yield from (sorted) merged groups

    We can get a deterministic and orderable key function from types by using repr(type(x)). Note that the 'type hierarchy' here is determined by the repr of the types themselves. A flaw in this method is that if two types have identical __repr__ (the types themselves, not the instances), you will 'confuse' types. This can be solved by using a key function that returns a tuple (repr(type), id(type)), but I have not implemented that in this solution.

    The advantage of my method over Bas Swinkel's is a cleaner handling of a group of un-orderable elements. We do not have quadratic behavior; instead, the function gives up after the first attempted ordering during sorted()).

    My method functions worst in the scenario where there are an extremely large number of different types in the iterable. This is a rare scenario, but I suppose it could come up.

    def py2sort(iterable):
            by_type_repr = lambda x: repr(type(x))
            iterable = sorted(iterable, key = by_type_repr)
            types = {type_: list(group) for type_, group in groupby(iterable, by_type_repr)}
    
            def merge_compatible_types(types):
                representatives = [(type_, items[0]) for (type_, items) in types.items()]
    
                def mergable_types():
                    for i, (type_0, elem_0) in enumerate(representatives, 1):
                        for type_1, elem_1 in representatives[i:]:
                             if _comparable(elem_0, elem_1):
                                 yield type_0, type_1
    
                def merge_types(a, b):
                    try:
                        types[a].extend(types[b])
                        del types[b]
                    except KeyError:
                        pass # already merged
    
                for a, b in mergable_types():
                    merge_types(a, b)
                return types
    
            def gen_from_sorted_comparable_groups(types):
                for _, items in types.items():
                    try:
                        items = sorted(items)
                    except TypeError:
                        pass #unorderable type
                    yield from items
            types = merge_compatible_types(types)
            return list(gen_from_sorted_comparable_groups(types))
    
        def _comparable(x, y):
            try:
                x < y
            except TypeError:
                return False
            else:
                return True
    
        if __name__ == '__main__':    
            print('before py2sort:')
            test = [2, -11.6, 3, 5.0, (1, '5', 3), (object, object()), complex(2, 3), [list, tuple], Fraction(11, 2), '2', type, str, 'foo', object(), 'bar']    
            print(test)
            print('after py2sort:')
            print(py2sort(test))
    

提交回复
热议问题