任务
你有一个包含列表(行)的列表,现在你想获得另一个列表,该列表包含了同样的行但是其中一些列被删除或者重新排序了。
解决方案
列表推导很适合这个任务。假设你有:
listOfRows = [ [1,2,3,4],[5,6,7,8],[9,10,11,12] ]
需要另一个有同样行的列表,但是其中第二列被删除了,第三和第四列则互换了位置我们用一个简单的列表推导来完成任务:
newList = [ [row[0],row[3],row[2] ] for row in listOfRows ]
还有一个方法同样具有可操作性,也许还更优雅一些,即采用一个辅助的序列(列表或元组),此序列的列索引位置正好是需要的顺序。这样,在外层对 listOfRows 进行循环操作的列表推导的内部,又加入了一个嵌套的对辅助序列进行循环操作的内层列表推导。
newList = [ [row[ci]for ci in(0,3,2)] for row in listofRows ]
讨论
我一般用列表的列表来代表二维的阵列。我把这些列表看做以二维阵列的“行”为元素的列表。我常常需要处理这种二维阵列的列,一般是重新排序,有时还会忽略列中的部分内容。尽管列表推导在别处大显神通,但粗略一看,它在这里似乎用处不大(至少我的第一感觉是这样)。
列表推导会产生一个新的列表,而不是修改现有的列表。当需要修改一个现有的列表时,最好的办法是将现有列表的内容赋值为一个列表推导。举个例子,假设要修改listOfRows,对于本节的任务,可以写成:
listOfRows[:] = [ [row[0],row[3],row[2] ] for row in listOfRows ]
正如本节解决方案的第二个例子所示,还可以考虑使用一个辅助的序列,其中包含的列索引按需要的顺序排列,这比第一个例子用硬编码指定顺序要好。你可能对嵌套的列表推导感到不放心,但事实上它比你想象的更简单安全。如果采用解决方案中的第二种方式,会同时获得更多的通用性,因为可以给辅助序列一个名字,并使用这个名字来对一些行列表进行重新排序,或者将其作为参数传递给一个函数,等等:
def pick_and_reorder_columns(listofRows, column_indexes):return [ [row[ci] for ci in column_indexes] for row in listofRows ]
Columns = 0,3,2
newlistOfPandas = pick_and_reorder_columns(oldListOfPandas, columns)
newListOfCats = pick_and_reorder_columns(oldListOfCats, columns)
上例所做的事情和本节前面的代码片段所做的事情完全一样,不过它操作两个独立的“旧的”列表,并分别获得两个对应的“新的”列表。追求最大程度的通用性是一种难以抵御的诱惑,但在这里,这个pick_and_reorder_columns所表现出的通用性似乎恰到好处。
最后一条,在前面用到的一些函数中,一些人喜欢用看上去更漂亮的方式来表示“内层”列表推导,而不采用简单直接的方式,比如:
[row[ci]for ci in column_indexes]
他们喜欢用内置的map函数以及row的特殊的,__getitem__方法(常被用作被绑定方法)来完成索引子任务,因此他们这样编写代码:
map(row.__getitem__,column_indexes)
具体到某些不同的 Python 的版本,这种看上去漂亮但又有点让人困扰的方法可能速度会快一些。但仍然认为体现简洁性的列表推导是最佳方式。