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','test.py'] 而如果使用列表推导式则可简化为: py_list = [f for f in file_list if f.endswith('.py')] print(py_list) # output ['foo.py','test.py'] 列表推导式的介绍网上资源很多,不再赘述。这里只强调,当你需要根据某个规则来构造一个列表时,首先应该想一想,能否使用简洁的列表推导式来实现该需求,否则再回到常规的方式。 为切片命名 Python 的列表切片使用起来非常方便,但有时也会影响代码可读性。例如有一个字符串: record = '..........19.6..........100..........' 19.6 为产品价格,100 为产品数量,那么计算总价格为: 但是如果这样写,可能过一段时间我们再来读代码时已经忘记了 record = '..........19.6..........100..........' price = slice(10,14) count = slice(24,27) total_price = float(record[price])*int(record[count]) slice 接收的参数格式为 排序 排序相关的任务通常由内置函数 sorted 完成。需要排序的元素一般存放在一个列表容器中,列表可以存放任意类型的元素,而 sorted 函数的 key 关键字使得我们能够轻松地指定元素排序的关键字,让排序变得异常简单。下面将给出几个常见的排序例子以说明 key 关键字的使用方法。注意 Python3 和 Python2 的排序方法不能通用,下面的例子只适用于 Python3 ,Python2 的排序方法未包含在本文中。 情况一 列表中的元素已经是可比较元素,直接将列表传入 sorted 函数即可返回一个已排序列表。默认为升序排列,降序排列可以指定 reverse 参数,例如: >>> l = [3,5,4,1,8] >>> sorted(l) [1,3,8] >>> sorted(l,reverse=True) [8,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','uid': 1004} ] l_v1 是一个元组列表, l_v2 是一个字典列表。对 l_v1 我们希望根据元组中第二个元素来排序,对 l_v2 我们希望根据字典的关键字 uid 进行排序。 sorted 函数接收一个关键字参数 key ,该参数指定一个可调用函数,函数返回一个值(只要是可比较的),那么 sorted 函数将根据返回的关键字对列表中的元素进行排序。 例如对上面的例子: >>> l_v1 = [('b',4)] >>> sorted(l_v1,key=lambda x: x[1]) [('a',('b',4)] >>> l_v2 = [ {'fname': 'Brian','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'},'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',key=itemgetter(1)) [('a',key=itemgetter('uid')) [ {'lname': 'Cleese','fname': 'Big'} ] 以上例子均是返回一个单一的值用于排序关键字,前面说过,关键字 key 接收的函数可以返回任意的可比较对象。例如在 python 中,元组是可以比较的。对元组的比较规则为首先比较元组中第一个位置上的元素,如果相等,在比较第二个位置上的元素,依次类推。回到 l_v2 的例子,假设现在需求变了,我们首先对 lname 对应的值排序,如果 lname 对应的值相等,那么再根据 fname 确定其顺序。 >>> l_v2 = [ {'fname': 'Brian',key=lambda x: (x['lname'],x['fname'])) [ {'lname': 'Beazley',{'lname': 'Cleese','fname': 'Big'},'fname': 'Brian'} ] 这个例子中,lambda 函数返回的不再是一个标量值,而是一个元组 同样使用 itemgetter 函数也是可以的,且性能会略有提升。此外我觉得 itemgetter 比 lambda 更加简洁和可读一点。 >>> l_v2 = [ {'fname': 'Brian',key=itemgetter('lname','fname')) [ {'lname': 'Beazley','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('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: John] attrgetter 与 itemgetter 用法完全一致,只是 itemgetter 用于获取某个位置索引或者字典关键字的取值,而 attrgetter 用于获取对象的属性值。 PS:sorted 返回的是原始列表的一个已排序的副本,而原始列表的顺序并没有任何变化。如果你只想就地排序(即排序原始列表本身),则直接调用 list 的 sort 方法即可: 对序列中的元素进行分组 和排序类似,现想根据列表中元素的某个关键字分组,使关键字相同的元素分到同一组,并可以对分好的组进行进一步处理。例如有如下的一个列表: 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',{'address': '1060 W ADDISON',{'address': '4801 N BROADWAY',{'address': '1039 W GRANVILLE',] 列表的元素为字典,现想根据字典的 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能有所帮助,如果有疑问大家可以留言交流,谢谢大家对编程小技巧的支持。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |