02.编程学习--Python 排序

文:郑元春
一个有着美好梦想的coder!

人生苦短,我用Python。

P.S. 个人认为,查找和排序是算法的核心,同时又是在实际应用中使用率最高。今天就说说Python里面的排序吧!


ox01:string的排序函数 -- s.sort()

s.sort([cmp[, key[, reverse]]])
sort the items of s in place

喜欢原版英文文档的同学就直接看原版的英文文档吧(为了方便阅读,我把英文原版文档放在了本文最后面),文档还是看原版的比较准确。

sort()函数和reverse()函数都是原地操作的(就是不会返回数据,直接在原有的数据上面操作,执行sort或者是reverse之后,你原始的数据将会被改变)。除了最基本的功能之外,sort()函数还有可选择的参数来完成一些高级或是定制的排序操作。排序的核心就是你定义的比较规则(cmp函数),只有比较才能分出高低来。cmp是用户自己指定比较规则,这个函数会接受两个参数(也就是list的element),通过比较你的第一个参数与第二个参数的关系(小,等于,大)规则函数会返回负数,0或正数。

  • 默认情况下,直接sort()

将字符串list排序,注意的是需要注意大小写问题,大写字符要比相应的小写字符靠前,原因是大写字符的Ascii值要靠前。Ascii[A]=65, Ascii[a]=97.

#假设我们有一个字符串list(网站的所有用户的用户名),现在需要按照字母表顺序进行排序。
usernameList=['Lily','Nancy','John','Micky','Andrew','XiaoMingZhang','LiHua']
usernameList.sort()
#结果是:
['Andrew', 'John', 'LiHua', 'Lily', 'Micky', 'Nancy', 'XiaoMingZhang']

#如果是想按照倒序排的话,需要将reverse设置为True
usernameList=['Lily','Nancy','John','Micky','Andrew','XiaoMingZhang','LiHua']
usernameList.sort(reverse=True)
#结果是:
['XiaoMingZhang', 'Nancy', 'Micky', 'Lily', 'LiHua', 'John', 'Andrew']

#大小写混乱的情况
usernameList=['lily','Nancy','john','micky','andrew','XiaoMingZhang','LiHua']
usernameList.sort()
#结果是(大写的先排):
['LiHua', 'Nancy', 'XiaoMingZhang', 'andrew', 'john', 'lily', 'micky']
  • 选取部分字符排序(注意是字符,不是字符串)

这时候你会看到排序的规则是按照第一个字符字典序排列,当第一个字符一样的情况下就去排第二个字符,依次类推。有时候我们需要按照某个位置排序,或是将字符串某个位置之后的排序(因为有的情况下所有的字符串会有一个相同/不同的前缀,比如区号,自定义代码等会对排序造成干扰)。这时候就使用key来指定参加排序的关键字了。

usernameList=[
'EN_lily',
'EN_Nancy',
'EN_John',
'EN_Micky',
'EN_Andrew',
'CN_XiaoMingZhang',
'CN_LiHua']

usernameList.sort()
#结果是:
['CN_LiHua',
 'CN_XiaoMingZhang',
 'EN_Andrew',
 'EN_John',
 'EN_Micky',
 'EN_Nancy',
 'EN_lily']

usernameList.sort(key=lambda x: x[3:])
#结果是:
['EN_Andrew',
 'EN_John',
 'CN_LiHua',
 'EN_Micky',
 'EN_Nancy',
 'CN_XiaoMingZhang',
 'EN_lily']

同时,前面提到的字符大小写问题就可以解决了,这里的key的功能更像是一个map函数的功能,我先将原始的待排序的数据做下预处理,可以是挑选一部分字符排序 ,可以是将字符转换成统一大小写。也可以将字符映射成其他的,只要是你映射之后可以被cmp规则处理就行。

  • 指定正序还是逆序

reverse参数指定的就是正序还是逆序,True为正序。在介绍第一个参数的时候已经介绍过了。

总结:

对于字符串的排序更多用在文本处理或者是简单的操作中,因为上面举得例子中如果在互联网应用中的话,这些操作在数据库就能够直接排序了,但是有的时候你必须使用Python的排序方法,比如你的后台开启多线程抓取数据,或是后台从不同的网站抓数据,可以直接就排序了。


0x02.sorted()

sorted(iterable[, cmp[, key[, reverse]]])
Return a new sorted list from the items in iterable.

对于sort()函数来说,一般用在list上。但Python内建的函数sorted()可用的范围就比较大了,适用于所有的iterable data。相比于sort()函数一个最大的不同就是sorted()函数不会在原始的数据上做"手脚",他会返回一个排好的list。

  1. iterable:是可迭代类型;

  2. cmp:用于比较的函数,比较什么由key决定;

  3. key:用列表元素的某个属性或函数进行作为关键字,有默认值,迭代集合中的一项;

  4. reverse:排序规则. reverse = True 降序 或者 reverse = False 升序,有默认值。

  5. 返回值:是一个经过排序的可迭代类型,与iterable一样。

其实大多数的使用方式和list的sort()函数一样。

  • 最简单的使用方式
>>> sorted([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5]
  • key 函数
    从2.4版本之后,list.sort()和sorted()函数都同时添加了key参数,指定iterable data中需要排序的数据。
>>> sorted("This is a test string from Andrew".split(), key=str.lower)
['a', 'Andrew', 'from', 'is', 'string', 'test', 'This']

key是个只接受单个参数的函数,它返回一个用来进行排序的key,之后的排序就是使用这个key进行排序,相当于上面提到的数据预处理了。官方文档里面说这项技术是fast的,因为对于每个输入这个key函数只执行一次。看完下面的例子你就会知道为什么我会在前面的段落中说这个key函数像是一个筛选操作。

student_tuples = [
    ('john', 'A', 15),
    ('jane', 'B', 12),
    ('dave', 'B', 10),
]
>>> sorted(student_tuples, key=lambda student: student[2])   # 年龄排序
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

学过数据库的同学一定会联想到order by 操作了吧!是的,在数据库操作中,我们可以select多column的数据,然后通过指定一行或是多行进行排序。同时不仅能指定tuple的排序key,还能对对象list的排序key进行指定(下面的例子)

>>> class Student:
        def __init__(self, name, grade, age):
            self.name = name
            self.grade = grade
            self.age = age
        def __repr__(self):
            return repr((self.name, self.grade, self.age))
>>> student_objects = [
    Student('john', 'A', 15),
    Student('jane', 'B', 12),
    Student('dave', 'B', 10),
]
>>> sorted(student_objects, key=lambda student: student.age)   
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
  • 更简单的使用key函数
    不得不说。Python真是为码农考虑的太周到了,上面通过对象的attribute进行key排序的已经很人性化了,但是,Python提供了itemgetter和attrgetter两个方法来进行更人性化的操作。废话不多说,看例子。
from operator import itemgetter, 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)]

如果是你需要排序的iterable data里面每个element是tuple的话,那就使用itemgetter函数进行key的筛选好了,如果iterable data里面的每个element是对象的话那就使用attrgetter函数进行key筛选好了,这样你连那个lambda函数的功夫也就省了(对于我这种小coder来说,两者好像没什么区别嘛)

  • key的多级排序
    上面提到过的数据库会运行coder指定多个column对select结果进行多级排序,同样Python的itemgetter和attrgetter也是支持多级排序的。
>>> 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)]

  • key的另一种“筛选”数据的方式
    其实,key的作用除了简单的“筛选”功能之外,还有很多的其他有意思的功能。将要排序的原始数据映射到另一个数据空间中,更像一个map函数的操作。
    通过结合operator.methodcaller()函数,下面的例子比较有趣:
 messages = ['critical!!!', 'hurry!', 'standby', 'immediate!!']
>>> sorted(messages, key=methodcaller('count', '!'))
['standby', 'hurry!', 'immediate!!', 'critical!!!']

从这个例子来看的话,对iterable data中的每个元素(这里是字符串)进行count("!")操作,将返回的“!”个数作为排序的依据。

  • 正序+逆序
    使用reverse参数就能控制逆序还是顺序了,和上一段用法一样。

总结:

Python的排序真的是让人喜欢的很啊,从最初始的基本功能,到各种高级功能,同时还支持key函数和自己定义的cmp函数,这些用户自行编写的功能函数很好的扩展了sort的功能,说其“三头六臂”也不为过啊!

  • key函数目的是找出排序依据,通过对原始数据预处理(选数据的一部分,计算元素个数等)来找出排血的基础,这才是key的真正意义吧,有的时候你的排序操作有可能就不是对原始数据做排序处理了。

  • cmp函数的目的是制定排序规则,通过用户自定义的排序函数,可以实现定制功能,只要你的返回符合他的规则就行了。

  • reverse参数简单的使用True或者是False就能翻转排序结果,用户体验很好。

文章的最后放上官方的文档作为原始的参考。

The sort() and reverse() methods modify the list in place for economy of space when sorting or reversing a large list. To remind you that they operate by side effect, they don’t return the sorted or reversed list.

The sort() method takes optional arguments for controlling the comparisons.

cmp specifies a custom comparison function of two arguments (list items) which should return a negative, zero or positive number depending on whether the first argument is considered smaller than, equal to, or larger than the second argument: cmp=lambda x,y: cmp(x.lower(), y.lower()). The default value is None.

key specifies a function of one argument that is used to extract a comparison key from each list element: key=str.lower. The default value is None.

reverse is a boolean value. If set to True, then the list elements are sorted as if each comparison were reversed.

In general, the key and reverse conversion processes are much faster than specifying an equivalent cmp function. This is because cmp is called multiple times for each list element while key and reverse touch each element only once. Use functools.cmp_to_key() to convert an old-style cmp function to a key function.

Changed in version 2.3: Support for None as an equivalent to omitting cmp was added.

Changed in version 2.4: Support for key and reverse was added.

Starting with Python 2.3, the sort() method is guaranteed to be stable. A sort is stable if it guarantees not to change the relative order of elements that compare equal — this is helpful for sorting in multiple passes (for example, sort by department, then by salary grade).


The optional arguments cmp, key, and reverse have the same meaning as those for the list.sort() method (described in section Mutable Sequence Types).

cmp specifies a custom comparison function of two arguments (iterable elements) which should return a negative, zero or positive number depending on whether the first argument is considered smaller than, equal to, or larger than the second argument: cmp=lambda x,y: cmp(x.lower(), y.lower()). The default value is None.

key specifies a function of one argument that is used to extract a comparison key from each list element: key=str.lower. The default value is None (compare the elements directly).

reverse is a boolean value. If set to True, then the list elements are sorted as if each comparison were reversed.

In general, the key and reverse conversion processes are much faster than specifying an equivalent cmp function. This is because cmp is called multiple times for each list element while key and reverse touch each element only once. Use functools.cmp_to_key() to convert an old-style cmp function to a key function.

The built-in sorted() function is guaranteed to be stable. A sort is stable if it guarantees not to change the relative order of elements that compare equal — this is helpful for sorting in multiple passes (for example, sort by department, then by salary grade).

参考:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容