python如何降序排列_Python:如何排序(sort)transform和convert的区别
⼀、前⾔
对Python的列表(list)有两个⽤于排序的⽅法:
⼀个是内建⽅法list.sort(),可以直接改变列表的内容:
>>> list1 = [9,8,7,6,5]>>>list1.sort()>>>list1
[5, 6, 7, 8, 9]
另⼀个是内建函数sorted(),它的特点是不改变原列表的内容,⽽是根据⼀个可迭代对象建⽴⼀个新的列表:
>>> list2 = [4,3,2,1]>>> list3 =sorted(list2)>>>list2
[4, 3, 2, 1]>>>list3
[1, 2, 3, 4]
⼆、基础排序
最简单的升序排序⾮常容易:直接调⽤sorted()函数就可以了,它返回⼀个新的列表:
>>> sorted([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5]
也可以使⽤列表本⾝的⽅法list.sort()去排序。它会改变list的内容,然后返回None作为执⾏的结果,以避免混淆。⼀般来说它没有sorted()那么⽅便,但是如果你不需要原来的列表的话,使⽤它在性能上会有轻微的提升。
>>> a = [5, 2, 3, 1, 4]>>>a.sort()>>>a
[1, 2, 3, 4, 5]
另⼀个区别就是,list.sort()⽅法只能⽤于列表,相对的,sorted()函数则适⽤于所有的可迭代对象,如:
>>> sorted({1: 'D', 2: 'B', 3: 'B', 4: 'E', 5: 'A'})
[1, 2, 3, 4, 5]
三、key函数
从Python2.4开始,⽆论是list.sort()还是sorted()都增加了⼀个key参数,指定⼀个在进⾏⽐较之前作⽤在每个列表元素上的函数。
例如,以下就是⼤⼩写不敏感的字符串⽐较:
>>> sorted("This is a test string from Andrew".split(), key=str.lower)
['a', 'Andrew', 'from', 'is', 'string', 'test', 'This']
key参数对应的值,必须是这样⼀个函数:接受⼀个参数然后返回⼀个⽤来排序的键。⽤这种技术来排序在速度上是⾮常快的,因为key函数恰好被每⼀个输⼊记录调⽤⼀次。
⼀种常⽤的模式是,在对复杂对象进⾏排序的时候,使⽤这个对象的索引作为排序的键,例如:
>>> student_tuples =[
... ('john', 'A', 15),
... ('jane', 'B', 12),
... ('dave', 'B', 10),
... ]>>> sorted(student_tuples, key=lambda student: student[2]) #sort by age
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
⽤对象的命名属性也是⼀样的效果,例如:
>>> classStudent:
...def __init__(self, name, grade, age):
... self.name=name
... ade=grade
... self.age=age
...def __repr__(self):
...returnrepr((self.name, ade, self.age))>>> student_objects =[
.
.. Student('john', 'A', 15),
... Student('jane', 'B', 12),
... Student('dave', 'B', 10),
... ]>>> sorted(student_objects, key=lambda student: student.age) #sort by age
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
四、使⽤Operator模块的函数
上⾯提到的key函数在排序中是很常⽤的,因此Python本⾝也提供了很多很⽅便的函数,让创建访问器函数变得更快、更容易。operater 模块提供的函数有operator.itemgetter(),operator.attrgetter(),另外从Python2.5开始新增了hodcaller()函数。
使⽤这些函数,上⾯的例⼦可以变得更简单和更快:
>>> from operator importitemgetter, attrgetter>>> sorted(student_tuples, key=itemgetter(2))
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]>>> sorted(student_objects, key=attrgetter('age'))
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
operator模块提供的⼏个函数允许多级别的排序。例如,根据grade和age进⾏排序:
>>> sorted(student_tuples, key=itemgetter(1,2))
[('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]>>> sorted(student_objects, key=attrgetter('grade', 'age'))
[('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]
>>> from operator importmethodcaller>>> messages = ['critical', 'hurry!', 'standby', 'immediate!!']>>> sorted(messages,
key=methodcaller('count', '!'))
['standby', 'hurry!', 'immediate!!', 'critical']
五、升序和降序
list.sort()和sorted()都接受⼀个reverse参数,这个参数的取值是⼀个布尔值,⽤来标记是否降序排序。例如,⽤age字段降序排列去获取学⽣信息:
>>> sorted(student_tuples, key=itemgetter(2), reverse=True)
[('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
>>> sorted(student_objects, key=attrgetter('age'), reverse=True)
[('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
六、排序稳定性及复杂排序
从Python2.2版本开始,排序都是稳定的,也就是说如果有两个记录他们的排序键相等,则排序前后他们的原始顺序是固定不变的。
>>> data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)]>>> sorted(data, key=itemgetter(0))
[('blue', 1), ('blue', 2), ('red', 1), ('red', 2)]
观察可以得知两个排序键同样是’blue’的记录,他们的原始顺序在排序前后没有改变,保证(‘blue’, 1)在(‘blue’, 2)前⾯。
这种极佳的属性可以让你⽤⼀系列排序的步骤去创建⼀种复杂的排序。例如,⽤grade字段降序,age字段升序去排序学⽣数据,age字段先排,grade字段后排。
>>> s = sorted(student_objects, key=attrgetter('age')) #sort on secondary key
>>> sorted(s, key=attrgetter('grade'), reverse=True) #now sort on primary key, descending
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
Python的Timsort算法可以⾼效率地进⾏多重排序,因为它能很好的利⽤数据集中已经存在的有序序列。
七、使⽤decorate-sort-undecorated⽼⽅法
这个习语是以它的三个步骤⽽被命名为decorate-sort-undecorate(装饰-排序-去装饰)的:
⾸先,原始的列表被装饰以⽣成新的值,这些值是⽤来控制排序顺序的。
然后,对被装饰过的列表进⾏排序。
最后,去掉装饰,以新的顺序创建⼀个列表,这个列表只包含原来列表中的值。
例如,使⽤DSU⽅法⽤学⽣数据中的grade字段对其进⾏排序:
>>> decorated = [(ade, i, student) for i, student inenumerate(student_objects)]>>>decorated.sort()>>> [student for grade, i, student in decorated] #undecorate
[('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
在这⾥,元组是按照字典序进⾏⽐较的,⾸先第⼀项被⽐较,如果他们相等则对第⼆项进⾏⽐较,以此类推。
不是在所有的情况下都需要在被装饰的列表中包含下标i,但是包含它会有两个好处:
让排序稳定——如果两个项拥有⼀样的键,那么他们在排序列表中的顺序不会改变。
原始的项不需要是可对⽐的,因为被装饰的元组的顺序最多被前⾯两个项决定。举个例⼦,原始列表可以包含不能直接排序的复数。
这个习语的另⼀个名字是Schwartzian transform,以Randal L. Schwartz进⾏命名,因为他⽤Per语⾔推⼴了这种⽅法。
对于⼤型的列表,以及那些计算对⽐关系的代价很昂贵的列表来说,在Python的2.4版本之前,DSU⼏乎是排序这类列表的最快的⽅法。但是在2.4及之后的版本,key函数就提供了⼀样的功能了。
⼋、使⽤cmp参数⽼⽅法
在这篇⽂章中提到的很多设计,其实都是Python2.4或之后的版本才给出的。在此之前,⽆论sorted()函数还是list.sort()⽅法都没有不带参的调⽤⽅式。相反的,所有Py2.x版本⽀持cmp参数,来处理⽤户指定的对⽐函数。
在Python3中,cmp参数是完全被移除了(这是对简化和统⼀这门语⾔作出的努⼒的⼀部分,另外也消除了富⽐较(rich comparisons )和__cmp()__魔术⽅法的冲突)。
在Python2中,sort()允许指定⼀个可选的函数,这个函数作为⽐较的⽤途被调⽤。这个函数必须带两个被⽤来⽐较的参数,然后返回⼀个值,如果⼩于是负数,如果相等是0,如果⼤于则返回正数。例如,我们可以这么做:
>>> defnumeric_compare(x, y):
...return x -y>>> sorted([5, 2, 4, 1, 3], cmp=numeric_compare)
[1, 2, 3, 4, 5]
或者你也可以反转对⽐的顺序:
>>> defreverse_numeric(x, y):
...return y -x>>> sorted([5, 2, 4, 1, 3], cmp=reverse_numeric)
[5, 4, 3, 2, 1]
从Python2.x导⼊代码到3.x的时候,如果你使⽤这种⽐较函数的时候,代码会报错,然后你就需要把它转为⼀个key函数了。以下的包装器可以让这个转化过程变得更简单:
defcmp_to_key(mycmp):'Convert a cmp= function into a key= function'
classK(object):def __init__(self, obj, *args):
self.obj=objdef __lt__(self, other):return mycmp(self.obj, other.obj) <0def __gt__(self, other):return mycmp(self.obj, other.obj) >0def __eq__(self, other):return mycmp(self.obj, other.obj) ==0def __le__(
self, other):return mycmp(self.obj, other.obj)
<=0def __ge__(self, other):return mycmp(self.obj, other.obj) >=0def __ne__(self, other):return mycmp(self.obj, other.obj)
!=0return K
想转化成⼀个key函数,直接包装旧的对⽐函数就可以了:
>>> sorted([5, 2, 4, 1, 3], key=cmp_to_key(reverse_numeric))
[5, 4, 3, 2, 1]
在Python2.7中,p_to_key()函数已经被增加到functools模块中了。
九、其他
对于浮点数的排序,使⽤locale.strxfrm()作为key函数,或者locale.strcoll()作为对⽐函数。
reverse参数依旧保持排序的稳定性。有趣的是,这个效果可以通过调⽤内建函数reversed()两次进⾏模拟:
>>> data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)]>>> standard_way = sorted(data, key=itemgetter(0), reverse=True)>>> double_reversed = list(reversed(sorted(reversed(data), key=itemgetter(0))))>>> assert standard_way
==double_reversed>>>standard_way
[('red', 1), ('red', 2), ('blue', 1), ('blue', 2)]
为⼀个类创建⼀种标准的排序顺序,只需要增加合适的富⽐较函数即可:
>>> Student.__eq__ = lambda self, other: self.age ==other.age>>> Student.__ne__ = lambda self, other: self.age !=other.age>>> Student.__lt__ = lambda self, other: self.age >> Student.__le__ = lambda self, other: self.age <=other.age>>> Student.__gt__ = lambda self, other: self.age >other.age>>> Student.__ge__ = lambda self, other: self.age
>=other.age>>>sorted(student_objects)
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
对于⼀般⽤途的⽐较,⼀般的⽅法建议定义全部六个富对⽐操作符。现在al_ordering() 类装饰器让这⼀切变得更加容易实现。
key函数不⼀定要直接基于被排序的对象。key函数同样可以访问外部的资源。举个例⼦,如果学⽣的年级被存储在⼀个字典之中,那它就可以⽤来为另⼀个单独的的学⽣的名字组成的列表进⾏排序:
>>> students = ['dave', 'john', 'jane']>>> grades = {'john': 'F', 'jane':'A', 'dave': 'C'}>>> sorted(students, key=grades.__getitem__)
['jane', 'dave', 'john']
以上内容翻译⾃Python2.7.15⽂档,Sorting HOW TO。(完)

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。