Python序列操作之进阶篇

简介

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能有所帮助,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • 从零学Python之入门(三)序列

    sequence 序列 sequence(序列)是一组有顺序的元素的集合 (严格的说,是对象的集合,但鉴于我们还没有引入"对象"概念,暂时说元素) 序列可以包含一个或多个元素,也可以没有任何元素. 我们之前所说的基本数据类型,都可以作为序列的元素.元素还可以是另一个序列,以及我们以后要介绍的其他对象. 序列有两种:tuple(定值表: 也有翻译为元组) 和 list (表) 复制代码 代码如下: >>>s1 = (2, 1.3, 'love', 5.6, 9, 12,

  • python基础教程之序列详解

    sequence 序列 sequence(序列)是一组有顺序的元素的集合 (严格的说,是对象的集合,但鉴于我们还没有引入"对象"概念,暂时说元素) 序列可以包含一个或多个元素,也可以没有任何元素. 我们之前所说的基本数据类型,都可以作为序列的元素.元素还可以是另一个序列,以及我们以后要介绍的其他对象. 序列有两种:tuple(定值表: 也有翻译为元组) 和 list (表) 复制代码 代码如下: >>>s1 = (2, 1.3, 'love', 5.6, 9, 12,

  • Python 序列的方法总结

    最近在做Python 的项目,特地整理了下 Python 序列的方法.序列sequence是python中最基本的数据结构,本文先对序列做一个简单的概括,之后简单讲解下所有序列都能通用的操作方法. 任何序列都可以引用其中的元素(item). 下面的内建函数(built-in function)可用于列表(表,定值表,字符串) #s为一个序列 len(s) 返回: 序列中包含元素的个数 min(s) 返回:序列中最小的元素 max(s) 返回:序列中最大的元素 all(s) 返回:True,若果所

  • Python中序列的修改、散列与切片详解

    前言 本文主要给大家介绍了关于Python中序列的修改.散列与切片的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. Vector类:用户定义的序列类型 我们将使用组合模式实现 Vector 类,而不使用继承.向量的分量存储在浮点数数组中,而且还将实现不可变扁平序列所需的方法. Vector 类的第 1 版要尽量与前一章定义的 Vector2d 类兼容. Vector类第1版:与Vector2d类兼容 Vector 类的第 1 版要尽量与前一章定义的 Vector2d

  • python中使用序列的方法

    本文实例讲述了python中使用序列的方法.分享给大家供大家参考.具体如下: 列表.元组和字符串都是序列,但是序列是什么,它们为什么如此特别呢?序列的两个主要特点是索引操作符和切片操作符.索引操作符让我们可以从序列中抓取一个特定项目.切片操作符让我们能够获取序列的一个切片,即一部分序列. #!/usr/bin/python # Filename: seq.py shoplist = ['apple', 'mango', 'carrot', 'banana'] # Indexing or 'Sub

  • 浅析python中的分片与截断序列

    序列概念 在分片规则里list.tuple.str(字符串)都可以称为序列,都可以按规则进行切片操作 切片操作 注意切片的下标0代表顺序的第一个元素,-1代表倒序的第一个元素:且切片不包括右边界,例如[0:3]代表元素0.1.2不包括3. l=['a','b','c','d',5] 1.获取列表的前3个元素 >>> l[0:3] ['a', 'b', 'c'] >>> l[:3] ['a', 'b', 'c'] 2.获取列表的后3个元素 >>> l[-

  • Python序列操作之进阶篇

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

  • Python序列循环移位的3种方法推荐

    第一种方法:特点是直接.容易理解,缺点是速度慢,只能实现循环左移. def demo(lst, k): temp = lst[:] for i in range(k): temp.append(temp.pop(0)) return temp 第二种方法:特点是速度快,并且自适应循环左移(k>0)和右移(k<0),缺点是涉及到算法,不容易理解. def demo(lst, k): x = lst[:k] x.reverse() y = lst[k:] y.reverse() r = x+y r

  • Python编程之序列操作实例详解

    本文实例讲述了Python编程之序列操作.分享给大家供大家参考,具体如下: #coding=utf8 ''''' 序列类型有着相同的访问模式:它的每一个元素可以通过指定一个偏移量的方式得到. 可以通过切片操作一次获得多个元素. 序列的下标偏移量是从0开始到总元素数减一结束. 标准类型操作符一般都能试用与所有的序列类型. 序列类型操作符: --------------------------------------------------------------------------- 序列操作

  • Python使用Pickle库实现读写序列操作示例

    本文实例讲述了Python使用Pickle库实现读写序列操作.分享给大家供大家参考,具体如下: 简介 pickle模块实现了用于对Python对象结构进行序列化和反序列化的二进制协议."Pickling"是将Python对象转换为字节流的过程,"unpickling"是反向操作,由此字节流二进制文件或字节对象)转换回对象结构. 模块方法 pickle.dump(obj, file, protocol=None, *, fix_imports=True) 将obj以二

  • Python进阶篇之正则表达式常用语法总结

    目录 正则表达式概述 1.点-匹配所有字符 2.星号-重复匹配任意次 3.加号-重复匹配多次 4. 花括号-匹配指定次数 5. 问号-贪婪模式和非贪婪模式 6.方括号-匹配几个字符之一 7.起始位置和单行.多行模式 8.括号-组选择 9.反斜杠-对元字符的转义 10.修饰符-可选标志 11.使用正则表达式切割字符串 正则表达式概述 正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配. Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模

  • 初学python的操作难点总结(新手必看篇)

    如下所示: 1 在cmd下 盘与盘之间的切换 直接 D或d: 就好 2 查找当前盘或者文件下面的目录 直接 dir 3 想在一个盘下进去一个文件夹,用cd空格目标文件 cd p 4 写文件的第一个字母后 按tab键自动补全 如果有多个p开头的则在按tab 会在所有之间切换 5 d:切盘 dir 查找目录 cd 进去目标文件(相当于双击) 6 往上走一层 cd .. 走两层 cd ../..(之间有无空格都行) 7 用python打开一个预先用记事本打好的txt 则先打开python f:\Dem

  • Python 数据处理库 pandas进阶教程

    前言 本文紧接着前一篇的入门教程,会介绍一些关于pandas的进阶知识.建议读者在阅读本文之前先看完pandas入门教程. 同样的,本文的测试数据和源码可以在这里获取: Github:pandas_tutorial. 数据访问 在入门教程中,我们已经使用过访问数据的方法.这里我们再集中看一下. 注:这里的数据访问方法既适用于Series,也适用于DataFrame. 基础方法:[]和. 这是两种最直观的方法,任何有面向对象编程经验的人应该都很容易理解.下面是一个代码示例: # select_da

  • Python序列对象与String类型内置方法详解

    本文实例讲述了Python序列对象与String类型内置方法.分享给大家供大家参考,具体如下: 前言 在Python数据结构篇中介绍了Python的序列类型数据结构,这次继续深入的学习序列和String类型对象的内建方法. 软件环境 系统 UbuntuKylin 14.04 软件 Python 2.7.3 IPython 4.0.0 序列类型 序列类型,即由整数进行索引的对象的有序集合.其中又可以分为下面两类: 可变序列:列表,支持元素的插入.删除.替换 不可变序列:元组.字符串 序列的操作方法

  • Python切片操作实例分析

    本文实例讲述了Python切片操作.分享给大家供大家参考,具体如下: 在很多编程语言中,针对字符串提供了截取函数,其实目的就是对字符串切片.Python没有针对字符串的截取函数,只需要切片操作就可以完成. 切片操作符是序列名后跟一个方括号,方括号中有3个可选的数字,并用冒号分割,数是可选的,而冒号是必须的. 切片操作符中的第一个数表示切片开始的位置,第二个数表示切片到哪里结束,第三个数表示切片步长. 如果不指定第一个数,Python就从序列首开始.如果没有指定第二个数,则Python会停止在序列

  • Python切片操作深入详解

    本文实例讲述了Python切片操作.分享给大家供大家参考,具体如下: 我们基本上都知道Python的序列对象都是可以用索引号来引用的元素的,索引号可以是正数由0开始从左向右,也可以是负数由-1开始从右向左. 在Python中对于具有序列结构的数据来说都可以使用切片操作,需注意的是序列对象某个索引位置返回的是一个元素,而切片操作返回是和被切片对象相同类型对象的副本. 如下面的例子,虽然都是一个元素,但是对象类型是完全不同的: >>> alist = [0, 1, 2, 3, 4, 5, 6

随机推荐