I wrote this method to be, first and foremost, flexible. This method implements what is referred to as a Schwartzian transform algorithm. This method does provide a mechanism to ensure stability in the sort. This version does a sort in place.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | def sortByAttrs(seq, attrs):
listComp = ['seq[:] = [(']
for attr in attrs:
listComp.append('seq[i].%s, ' % attr)
listComp.append('i, seq[i]) for i in xrange(len(seq))]')
exec('%s' % ''.join(listComp))
seq.sort()
seq[:] = [obj[-1] for obj in seq]
return
#
# begin test code
#
from random import randint
class a:
def __init__(self):
self.x = (randint(1, 5), randint(1, 5))
class b:
def __init__(self):
self.x = randint(1, 5)
self.y = (a(), a())
class c:
def __init__(self, arg):
self.x = arg
self.y = b()
if __name__ == '__main__':
aList = [c(1), c(2), c(3), c(4), c(5), c(6)]
print '\n...to be sorted by obj.y.y[1].x[1]'
print ' then, as needed, by obj.y.x'
print ' then, as needed, by obj.x\n\n ',
for i in range(6):
print '(' + str(aList[i].y.y[1].x[1]) + ',',
print str(aList[i].y.x) + ',',
print str(aList[i].x) + ') ',
sortByAttrs(aList, ['y.y[1].x[1]', 'y.x', 'x'])
print '\n\n...now sorted by listed attributes.\n\n ',
for i in range(6):
print '(' + str(aList[i].y.y[1].x[1]) + ',',
print str(aList[i].y.x) + ',',
print str(aList[i].x) + ') ',
print
#
# end test code
#
|
As demonstrated in the test code above, a list can be sorted by attribute, by dotted attribute, and by dotted attribute[index] forms.
The list/tuple of passed attributes can have an arbitrary length greater than zero. This allows for a very robust determination of comparison 'on the fly'. If an object in the list does not have a passed attribute, the method will throw (and not catch) an AttributeError.