Python序列操作之进阶篇


Posted in Python onDecember 08, 2016

简介

Python 的序列(sequence)通常指一个可迭代的容器,容器中可以存放任意类型的元素。列表和元组这两种数据类型是最常被用到的序列,python内建序列有六种,除了刚刚有说过的两种类型之外,还有字符串、Unicode字符串、buffer对像和最后一种xrange对像,这几种都是不常使用的。本文讲解了列表推导式、切片命名、列表元素排序、列表元素分组的使用方法。学习了 Python 基本的列表操作后,学习这些进阶的操作,让我们写出的代码更加优雅简洁和 pythonic 。

列表推导式

当我们想要根据某些规则来构造一个列表时,首先想到的应该是列表推导式。列表推导式简化了循环操作,例如我们想要从一个原始文件名列表中获取全部 .py 文件,在没有列表推导式的情况下,我们通常会这样做:

file_list = ['foo.py', 'bar.txt', 'spam.py', 'animal.png', 'test.py']
py_list = []
for file in file_list:
if file.endswith('.py'):
py_list.append(file)

print(py_list)
# output
['foo.py', 'spam.py', 'test.py']

而如果使用列表推导式则可简化为:

py_list = [f for f in file_list if f.endswith('.py')]
print(py_list)
# output
['foo.py', 'spam.py', 'test.py']

列表推导式的介绍网上资源很多,不再赘述。这里只强调,当你需要根据某个规则来构造一个列表时,首先应该想一想,能否使用简洁的列表推导式来实现该需求,否则再回到常规的方式。

为切片命名

Python 的列表切片使用起来非常方便,但有时也会影响代码可读性。例如有一个字符串:

record = '..........19.6..........100..........'

19.6 为产品价格,100 为产品数量,那么计算总价格为:

但是如果这样写,可能过一段时间我们再来读代码时已经忘记了 record[10:14] record[24:27] 切出来的究竟是什么?为了解决上述问题,可以给切片命个名来增强可读性。

record = '..........19.6..........100..........'
price = slice(10, 14)
count = slice(24, 27)
total_price = float(record[price])*int(record[count])

slice 接收的参数格式为 slice(stop)slice(start, stop[, step]) 。如果只接收了一个参数,则等价于切片语法 [:stop] ,如果接收两个参数,则等价于切片语法 [start:stop] ,如果接收三个参数,则等价于切片语法 [start:stop:step]

排序

排序相关的任务通常由内置函数 sorted 完成。需要排序的元素一般存放在一个列表容器中,列表可以存放任意类型的元素,而 sorted 函数的 key 关键字使得我们能够轻松地指定元素排序的关键字,让排序变得异常简单。下面将给出几个常见的排序例子以说明 key 关键字的使用方法。注意 Python3 和 Python2 的排序方法不能通用,下面的例子只适用于 Python3 ,Python2 的排序方法未包含在本文中。

情况一

列表中的元素已经是可比较元素,直接将列表传入 sorted 函数即可返回一个已排序列表。默认为升序排列,降序排列可以指定 reverse 参数,例如:

>>> l = [3,5,4,1,8]
>>> sorted(l)
[1, 3, 4, 5, 8]
>>> sorted(l, reverse=True)
[8, 5, 4, 3, 1]
>>>

情况二

需要排序的元素是一个元组或者字典,希望根据我指定的关键字来排序,例如有如下两个列表:

l_v1 = [('b',2),('a',1),('c',3),('d',4)]
l_v2 = [
 {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003},
 {'fname': 'David', 'lname': 'Beazley', 'uid': 1002},
 {'fname': 'John', 'lname': 'Cleese', 'uid': 1001},
 {'fname': 'Big', 'lname': 'Jones', 'uid': 1004}
]

l_v1 是一个元组列表, l_v2 是一个字典列表。对 l_v1 我们希望根据元组中第二个元素来排序,对 l_v2 我们希望根据字典的关键字 uid 进行排序。

sorted 函数接收一个关键字参数 key ,该参数指定一个可调用函数,函数返回一个值(只要是可比较的),那么 sorted 函数将根据返回的关键字对列表中的元素进行排序。

例如对上面的例子:

>>> l_v1 = [('b',2),('a',1),('c',3),('d',4)]
>>> sorted(l_v1, key=lambda x: x[1])
[('a', 1), ('b', 2), ('c', 3), ('d', 4)]
>>> l_v2 = [
{'fname': 'Brian', 'lname': 'Jones', 'uid': 1003},
{'fname': 'David', 'lname': 'Beazley', 'uid': 1002},
{'fname': 'John', 'lname': 'Cleese', 'uid': 1001},
{'fname': 'Big', 'lname': 'Jones', 'uid': 1004}
]
>>> sorted(l_v2, key=lambda x: x['uid'])
[{'lname': 'Cleese', 'uid': 1001, 'fname': 'John'}, {'lname': 'Beazley', 'uid': 1002, 'fname': 'David'}, {'lname': 'Jones', 'uid': 1003, 'fname': 'Brian'}, {'lname': 'Jones', 'uid': 1004, 'fname': 'Big'}]

这里 lambda 函数是一个常用的技巧。lambda 关键字后边的 x 是该函数接收的参数,冒号后边的表达式是该函数的返回值。对 l_v1 来说,传递给参数 x 的就是每一个元组,其返回元组的第二个元素用于排序;对 l_v2 来说,传递给参数 x 的就是列表中的每一个字典元素,其返回字典中 uid 对应的值用于排序。

除了使用匿名函数 lambda 这种通用的方法外,Python 标准库 operator 为我们提供了一个 itemgetter 函数替代我们写的 lambda 函数,且其性能会比使用 lambda 函数略有提升。

>>> from operator import itemgetter
>>> l_v1 = [('b',2),('a',1),('c',3),('d',4)]
>>> sorted(l_v1, key=itemgetter(1))
[('a', 1), ('b', 2), ('c', 3), ('d', 4)]
>>> l_v2 = [
{'fname': 'Brian', 'lname': 'Jones', 'uid': 1003},
{'fname': 'David', 'lname': 'Beazley', 'uid': 1002},
{'fname': 'John', 'lname': 'Cleese', 'uid': 1001},
{'fname': 'Big', 'lname': 'Jones', 'uid': 1004}
]
>>> sorted(l_v2, key=itemgetter('uid'))
[
{'lname': 'Cleese', 'uid': 1001, 'fname': 'John'}, 
{'lname': 'Beazley', 'uid': 1002, 'fname': 'David'}, 
{'lname': 'Jones', 'uid': 1003, 'fname': 'Brian'}, 
{'lname': 'Jones', 'uid': 1004, 'fname': 'Big'}
]

以上例子均是返回一个单一的值用于排序关键字,前面说过,关键字 key 接收的函数可以返回任意的可比较对象。例如在 python 中,元组是可以比较的。对元组的比较规则为首先比较元组中第一个位置上的元素,如果相等,在比较第二个位置上的元素,依次类推。回到 l_v2 的例子,假设现在需求变了,我们首先对 lname 对应的值排序,如果 lname 对应的值相等,那么再根据 fname 确定其顺序。

>>> l_v2 = [
 {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003},
 {'fname': 'David', 'lname': 'Beazley', 'uid': 1002},
 {'fname': 'John', 'lname': 'Cleese', 'uid': 1001},
 {'fname': 'Big', 'lname': 'Jones', 'uid': 1004}
]
>>> sorted(l_v2, key=lambda x: (x['lname'], x['fname']))
[
 {'lname': 'Beazley', 'uid': 1002, 'fname': 'David'}, 
 {'lname': 'Cleese', 'uid': 1001, 'fname': 'John'}, 
 {'lname': 'Jones', 'uid': 1004, 'fname': 'Big'}, 
 {'lname': 'Jones', 'uid': 1003, 'fname': 'Brian'}
]

这个例子中,lambda 函数返回的不再是一个标量值,而是一个元组 (x['lname'], x['fname']) ,根据元组的比较规则,首先根据元组的第一个位置上的元素 x['lname'] 的大小排序,由于列表中有两个字典其 lname 对应的值都为 Jones,因此再根据元组第二个位置的元素 x['fname'] 的值排序,由于 Big 比 Brian 要小(按字母顺序依次比较),所以 Big 排在了前面。

同样使用 itemgetter 函数也是可以的,且性能会略有提升。此外我觉得 itemgetter 比 lambda 更加简洁和可读一点。

>>> l_v2 = [
 {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003},
 {'fname': 'David', 'lname': 'Beazley', 'uid': 1002},
 {'fname': 'John', 'lname': 'Cleese', 'uid': 1001},
 {'fname': 'Big', 'lname': 'Jones', 'uid': 1004}
]
>>> sorted(l_v2, key=itemgetter('lname', 'fname'))
[
 {'lname': 'Beazley', 'uid': 1002, 'fname': 'David'}, 
 {'lname': 'Cleese', 'uid': 1001, 'fname': 'John'}, 
 {'lname': 'Jones', 'uid': 1004, 'fname': 'Big'}, 
 {'lname': 'Jones', 'uid': 1003, 'fname': 'Brian'}
]

情况三

需要排序的元素是一个 Python 对象,我们希望根据其某个属性值来排序。例如一个存放 User 对象的列表如下,根据其 name 属性排序:

class User:
 def __init__(self, name):
  self.name = name
def __str__(self):
return 'User: %s' % self.name

__repr__ = __str__ # 为了能够让 User 在解释器中显示为 'User: name' 的格式

user_list = [User('John'), User('David'), User('Big'), User('Alen')]

方法与前面的一样,定义一个函数返回 User 的 name 属性的值,把该函数传给 sorted 的 key 参数。

>>> user_list = [User('John'), User('David'), User('Big'), User('Alen')]
>>> sorted(user_list, key=lambda x: x.name)
>>> sorted(user_list, key=lambda x: x.name)
[User: Alen, User: Big, User: David, User: John]

但是,itemgetter 方法不再起作用,取而代之的是 attrgetter 方法。

>>> sorted(user_list, key=attrgetter('name'))
[User: Alen, User: Big, User: David, User: John]

attrgetter 与 itemgetter 用法完全一致,只是 itemgetter 用于获取某个位置索引或者字典关键字的取值,而 attrgetter 用于获取对象的属性值。

PS:sorted 返回的是原始列表的一个已排序的副本,而原始列表的顺序并没有任何变化。如果你只想就地排序(即排序原始列表本身),则直接调用 list 的 sort 方法即可:list.sort() 。其用法与 sorted 函数一样,只是该函数没有返回值,调用后原始列表已变为一个已排序列表。

对序列中的元素进行分组

和排序类似,现想根据列表中元素的某个关键字分组,使关键字相同的元素分到同一组,并可以对分好的组进行进一步处理。例如有如下的一个列表:

rows = [
 {'address': '5412 N CLARK', 'date': '07/01/2012'},
 {'address': '5148 N CLARK', 'date': '07/04/2012'},
 {'address': '5800 E 58TH', 'date': '07/02/2012'},
 {'address': '2122 N CLARK', 'date': '07/03/2012'},
 {'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'},
 {'address': '1060 W ADDISON', 'date': '07/02/2012'},
 {'address': '4801 N BROADWAY', 'date': '07/01/2012'},
 {'address': '1039 W GRANVILLE', 'date': '07/04/2012'},
]

列表的元素为字典,现想根据字典的 date 分组,使日期( date )相同的元素分到一个组。Python 的 itertools 模块中的 groupby 函数可以很好地解决该问题。为了使用 groupby 函数,首先需要对列表排序:

>>> from operator import itemgetter
>>> sorted_rows = sorted(rows, key=itemgetter('date'))

groupby 也和 sorted 一样有一个 key 关键字参数,其接收一个可调用函数,该函数返回的值被用做分组的关键字,其用法和 sorted 的 key 关键字参数一样 。

>>> for date, items in groupby(sorted_rows, key=itemgetter('date')):
 print(date)
 for i in items:
  print(' ', i)
07/01/2012
{'address': '5412 N CLARK', 'date': '07/01/2012'}
{'address': '4801 N BROADWAY', 'date': '07/01/2012'}
07/02/2012
{'address': '5800 E 58TH', 'date': '07/02/2012'}
{'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'}
{'address': '1060 W ADDISON', 'date': '07/02/2012'}
07/03/2012
{'address': '2122 N CLARK', 'date': '07/03/2012'}
07/04/2012
{'address': '5148 N CLARK', 'date': '07/04/2012'}
{'address': '1039 W GRANVILLE', 'date': '07/04/2012'}

可以看到 groupby 返回的值分别是用于分组的关键字对应的值和该组的全部成员。groupby 实际返回一个生成器,通过迭代即可分别对各组进行处理。值得注意的一点是,分组前对列表排序这一步必不可少,否则对于非紧邻的元素即使其值相同也会被分在不同组。

总结

以上就是关于python序列进阶篇的全部内容,希望本文的内容对大家学习或者使用python能有所帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
在Python的Django框架中更新数据库数据的方法
Jul 17 Python
Python数据类型详解(二)列表
May 08 Python
Python递归函数定义与用法示例
Jun 02 Python
对pandas中to_dict的用法详解
Jun 05 Python
解决Mac安装scrapy失败的问题
Jun 13 Python
Python实现base64编码的图片保存到本地功能示例
Jun 22 Python
Python3中详解fabfile的编写
Jun 24 Python
python实现定时发送qq消息
Jan 18 Python
Python切片操作去除字符串首尾的空格
Apr 22 Python
Django框架登录加上验证码校验实现验证功能示例
May 23 Python
Jupyter加载文件的实现方法
Apr 14 Python
python获取本周、上周、本月、上月及本季的时间代码实例
Sep 08 Python
利用Python破解验证码实例详解
Dec 08 #Python
详解使用python crontab设置linux定时任务
Dec 08 #Python
Python 正则表达式入门(中级篇)
Dec 07 #Python
Python 正则表达式入门(初级篇)
Dec 07 #Python
Python标准库06之子进程 (subprocess包) 详解
Dec 07 #Python
利用 Monkey 命令操作屏幕快速滑动
Dec 07 #Python
Python深入06——python的内存管理详解
Dec 07 #Python
You might like
咖啡的传说和历史
2021/03/03 新手入门
php中用文本文件做数据库的实现方法
2008/03/27 PHP
thinkphp如何获取客户端IP
2015/11/03 PHP
PHP实现负载均衡下的session共用功能
2018/04/17 PHP
Firefox 无法获取cssRules 的解决办法
2006/10/11 Javascript
jquery插件 autoComboBox 下拉框
2010/12/22 Javascript
js批量设置样式的三种方法不推荐使用with
2013/02/25 Javascript
javascript的内存管理详解
2013/08/07 Javascript
javascript实现表格增删改操作实例详解
2015/05/15 Javascript
JavaScript判断FileUpload控件上传文件类型
2015/09/28 Javascript
全面解析Bootstrap表单使用方法(表单控件状态)
2015/11/24 Javascript
JS实现兼容火狐及IE iframe onload属性的遮罩层隐藏及显示效果
2016/08/23 Javascript
JavaScript每天必学之基础知识
2016/09/17 Javascript
setTimeout函数的神奇使用
2017/02/26 Javascript
JS脚本实现网页自动秒杀点击
2018/01/11 Javascript
在ES5与ES6环境下处理函数默认参数的实现方法
2018/05/13 Javascript
Angularjs中date过滤器失效的问题及解决方法
2018/07/06 Javascript
微信小程序wx:for循环的实例详解
2018/10/07 Javascript
这15个Vue指令,让你的项目开发爽到爆
2019/10/11 Javascript
element-ui中按需引入的实现
2019/12/25 Javascript
用Python写脚本,实现完全备份和增量备份的示例
2018/04/29 Python
使用Django启动命令行及执行脚本的方法
2018/05/29 Python
Python3.4学习笔记之 idle 清屏扩展插件用法分析
2019/03/01 Python
python常用库之NumPy和sklearn入门
2019/07/11 Python
详细分析Python collections工具库
2020/07/16 Python
django有哪些好处和优点
2020/09/01 Python
Square Off美国/加拿大:世界上最聪明的国际象棋棋盘
2018/12/06 全球购物
PHP两种查询函数array/row的区别
2013/06/03 面试题
财会自我鉴定范文
2013/12/27 职场文书
推广普通话共筑中国梦演讲稿
2014/09/21 职场文书
工作失误检讨书(3篇)
2014/10/11 职场文书
西柏坡观后感
2015/06/08 职场文书
优秀的商业计划书,让融资一步到位
2019/05/07 职场文书
创作书写之导游词实用技巧分享(干货)
2019/12/20 职场文书
用Python提取PDF表格的方法
2021/04/11 Python
详细介绍python类及类的用法
2021/05/31 Python