Here's a solution which applies the reverse as -1 and 1 for True and False respectively, to the ordinaled version of each attribute - since it's likely that string attributes are more than one character. The idea was to create a key function that can be passed to sorted().
Edit: Here's the version with multilevel_order applying different reversal rules (note the name change):
def multilevel_order(reverse):
multipliers = tuple(-1 if trufal else 1 for trufal in reverse)
def key_func(item):
return tuple(tuple(map(m.__mul__, map(ord, attr))) for attr, m in zip(item, multipliers))
return key_func
Results:
items = [('a', 'b', 'c'), ('a', 'c', 'b'), ('a', 'b', 'b'),
('b', 'a', 'a'), ('b', 'a', 'b'), ('b', 'b', 'b')]
reverse = (True, False, True)
expected = [('b', 'a', 'b'), ('b', 'a', 'a'), ('b', 'b', 'b'),
('a', 'b', 'c'), ('a', 'b', 'b'), ('a', 'c', 'b')]
print(sorted(items, key=multilevel_order(reverse)) == expected)
# True
items = [('a', 'c'), ('b', 'a'), ('a', 'b'), ('b', 'b'), ('a', 'a')]
reverse = (True, False) # first level descending, second level ascending
expected = [('b', 'a'), ('b', 'b'), ('a', 'a'), ('a', 'b'), ('a', 'c')]
print(sorted(items, key=multilevel_order(reverse)) == expected)
# True
Original answer: I've also used slightly modified values of items to make the effect of ordinaled clearer.
items = [('a', 'bb', 'ccc'), ('a', 'cc', 'bbb'), ('a', 'bb', 'bbb'),
('b', 'aa', 'aaa'), ('b', 'aa', 'bbb'), ('b', 'bb', 'bbb')]
reverse = (True, False, True)
multipliers = tuple(-1 if trufal else 1 for trufal in reverse)
def multi_level_key(item):
ordinaled = (map(ord, attr) for attr in item)
# this multiplication step could have been included above
return tuple(tuple(map(m.__mul__, o)) for m, o in zip(multipliers, ordinaled))
Example output of multi_level_key():
[multi_level_key(item) for item in items]
# output:
[((-97,), (98, 98), (-99, -99, -99)),
((-97,), (99, 99), (-98, -98, -98)),
((-97,), (98, 98), (-98, -98, -98)),
((-98,), (97, 97), (-97, -97, -97)),
((-98,), (97, 97), (-98, -98, -98)),
((-98,), (98, 98), (-98, -98, -98))]
Using that as the key for sorted():
sorted(items, key=multi_level_key)
# output:
[('b', 'aa', 'bbb'),
('b', 'aa', 'aaa'),
('b', 'bb', 'bbb'),
('a', 'bb', 'ccc'),
('a', 'bb', 'bbb'),
('a', 'cc', 'bbb')]
Potential improvement: I moved multipliers out of the key function so that it's only calculated once. Ideally, that should be put in a closure to multi_level_key() so that it can be used with other reversal rules without needing to write a new function.