#!/usr/bin/env python3 from operator import itemgetter from timeit import default_timer as timer import json from random import randint xs = [ { 'col0': randint(0, 500000), 'col1': randint(0, 500000), 'col2': randint(0, 500000), 'col3': randint(0, 500000), 'col4': randint(0, 500000), 'col5': randint(0, 500000), 'col6': randint(0, 500000), 'col7': randint(0, 500000), } for i in range(500000) ] ## `my_single_pass_sort` works better when fewer columns need be reversed: ## ## - when no column needs to be reversed, `my_single_pass_sort` takes about ## 25% time of `single_pass_sort`; ## ## - when 1 column need to be reversed, `my_single_pass_sort` takes about ## 80% time of `single_pass_sort`; ## ## - when all columns need to be reversed, `my_single_pass_sort` takes about ## 120% time of `single_pass_sort`; specs = [ (itemgetter('col0'), False), (itemgetter('col1'), False), (itemgetter('col2'), False), (itemgetter('col3'), False), (itemgetter('col4'), False), (itemgetter('col5'), False), (itemgetter('col6'), False), (itemgetter('col7'), False), ] ## `multi_pass_sort` is usually much faster (2x) than the other two; def multi_pass_sort(xs, specs): ''' ''' for key, reverse in reversed(specs): xs.sort(key=key, reverse=reverse) def _sorter(specs): keyfuncs, reversers = zip(*specs) class Wrapper(object): def __init__(self, obj): self.keys = tuple(f(obj) for f in keyfuncs) def __lt__(x, y): for a, b, r in zip(x.keys, y.keys, reversers): if a < b: return not r if b < a: return r return False # all the keys are equal return Wrapper def single_pass_sort(xs, specs): ''' ''' xs.sort(key=_sorter(specs)) def my_single_pass_sort(xs, specs): ''' ''' def keygen(specs): class _: def __init__(self, v): self.v = v def __eq__(self, o): return o.v == self.v def __lt__(self, o): return o.v < self.v return lambda x: [ _(k(x)) if r else k(x) for k, r in specs ] xs.sort(key=keygen(specs)) t = timer() #multi_pass_sort(xs, specs) #single_pass_sort(xs, specs) #my_single_pass_sort(xs, specs) print(timer() - t) #print(json.dumps(xs, indent=4))