从美学角度和绩效角度来看,根据条件将项目列表拆分为多个列表的最佳方法是什么?相当于:
good = [x for x in mylist if x in goodvals]
bad = [x for x in mylist if x not in goodvals]
有没有更优雅的方式来做到这一点?
更新:这是实际的用例,以便更好地解释我正在尝试做的事情:
# files looks like: [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ... ]
IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
images = [f for f in files if f[2].lower() in IMAGE_TYPES]
anims = [f for f in files if f[2].lower() not in IMAGE_TYPES]
27 回答
该代码完全可读,非常清晰!
再次,这很好!
使用集合可能会有轻微的性能改进,但这是一个微不足道的差异,我发现列表理解更容易阅读,并且您不必担心订单被搞砸,重复删除等等 .
事实上,我可能会向后退一步,只需使用一个简单的for循环:
列表理解或使用
set()
是好的,直到你需要添加一些其他检查或其他逻辑 - 比如说你要删除所有0字节jpeg,你只需要添加类似的东西......这是懒惰的迭代器方法:
它每个项目评估一次条件并返回两个生成器,首先从条件为真的序列中产生值,另一个产生假值 .
因为它很懒,你可以在任何迭代器上使用它,甚至是无限的迭代器:
通常虽然非惰性列表返回方法更好:
编辑:对于您通过某个键将项目拆分到不同列表的更具体的用法,继承了一个通用函数:
用法:
所有提出的解决方案的问题在于它将扫描并应用过滤功能两次 . 我会做一个像这样的简单小函数:
这样你就不会处理任何两次而且也不会重复代码 .
我接受它 . 我提出了一个惰性的单遍
partition
函数,它保留了输出子序列中的相对顺序 .1.要求
我认为要求是:
维护元素的相对顺序(因此,没有集合和字典)
仅为每个元素评估条件一次(因此不使用(
i
)filter
或groupby
)允许任意序列的延迟消耗(如果我们能够预先计算它们,那么天真的实现也可能是可接受的)
2.拆分库
我的
partition
函数(下面介绍)和其他类似的函数使它成为一个小型库:它可以通过PyPI正常安装:
要根据条件拆分列表,请使用
partition
函数:3.解析分区功能
在内部,我们需要同时构建两个子序列,因此仅消耗一个输出序列将强制另一个也被计算 . 我们需要在用户请求(存储已处理但尚未请求的元素)之间保持状态 . 为了保持状态,我使用两个双端队列(
deques
):SplitSeq
班负责管家:魔术发生在它的
.getNext()
方法中 . 它几乎就像迭代器的.next()
,但允许指定我们想要的那种元素 . 在场景后面,它不会丢弃被拒绝的元素,而是将它们放入两个队列中的一个:最终用户应该使用
partition
函数 . 它需要一个条件函数和一个序列(就像map
或filter
),并返回两个生成器 . 第一个生成器构建条件成立的元素的子序列,第二个构建补充子序列 . 迭代器和生成器允许甚至长或无限序列的惰性分裂 .我选择测试函数作为第一个促进将来部分应用的参数(类似于
map
和filter
如何将测试函数作为第一个参数) .First go (OP前编辑):使用集合:
这对可读性(IMHO)和性能都有好处 .
Second go (后OP编辑):
创建一个好的扩展列表作为一组:
这将提高性能 . 否则,你看到的对我来说很好 .
我基本上喜欢安德斯的方法,因为它很一般 . 这是一个将分类程序放在第一位(匹配过滤器语法)并使用defaultdict(假定已导入)的版本 .
itertools.groupby几乎可以执行您想要的操作,但它需要对项目进行排序以确保您获得单个连续范围,因此您需要先按键排序(否则您将为每种类型获得多个交错组) . 例如 .
得到:
与其他解决方案类似,可以将键功能定义为分成您想要的任意数量的组 .
就个人而言,我喜欢你引用的版本,假设你已经有一个
goodvals
列表 . 如果不,就像是:当然,这与使用像你最初的列表理解非常相似,但是使用函数而不是查找:
总的来说,我发现列表理解的美学非常令人愉悦 . 当然,如果你不需要重复,那么在集合上使用
intersection
和difference
方法也会很有效 .如果你想在FP风格:
不是最易读的解决方案,但至少只通过mylist迭代一次 .
检查this
我认为基于N条件分割可迭代的概括是很方便的
例如:
如果元素可能满足多个条件,则删除中断 .
有时,看起来列表理解不是最好用的!
我根据人们对这个主题给出的答案进行了一点测试,并在随机生成的列表中进行了测试 . 这是列表的生成(可能有更好的方法,但不是重点):
现在我们开始
使用cmpthese函数,最好的结果是dbr答案:
这个问题的又一个解决方案 . 我需要一个尽可能快的解决方案 . 这意味着列表上只有一次迭代,最好是O(1),用于将数据添加到结果列表之一 . 这与sastanin提供的解决方案非常相似,只是更短:
然后,您可以通过以下方式使用该功能:
如果您对得到的
deque
对象不满意,您可以轻松地将其转换为list
,set
,无论您喜欢什么(例如list(lower)
) . 转换速度要快得多,直接构建列表 .此方法保持项目的顺序以及任何重复项 .
要获得性能,请尝试
itertools
.见itertools.ifilter或imap .
有时您不需要列表的另一半 . 例如:
受@ gnibbler的great (but terse!) answer的启发,我们可以应用该方法映射到多个分区:
然后
splitter
可以如下使用:这适用于两个以上具有更复杂映射的分区(以及迭代器):
或者使用字典来映射:
如果你坚持聪明,你可以采取Winden的解决方案,只是有点虚假的聪明:
如果您只关心上面的一些方法(甚至是您自己的方法)在单个函数中使用,那么您的关注点不是为一个只需要语义的操作使用两行代码:
它不是一个惰性eval方法,它在列表中迭代两次,但它允许您在一行代码中对列表进行分区 .
这里已经有很多解决方案,但另一种方法是 -
只在列表上迭代一次,看起来更加pythonic,因此对我来说可读 .
我采用2遍方法,将谓词的评估与过滤列表分开:
这对性能有什么好处(除了在
iterable
的每个成员上只评估一次pred
之外),它将大量逻辑移出解释器并进入高度优化的迭代和映射代码 . 这可以加速长迭代的迭代,如in this answer所述 .表达性方面,它利用了理解和映射等富有表现力的习语 .
解决方案
测试
我最喜欢的食谱是:
简单,快速,可读; Python本来就是这样的 .
通过将
goodvals
变为set
(使用哈希表)而不是tuple
,我们获得超快速查找 .mylist
中的每个项目仅检查一次 . 这有助于加快速度 ._ =
是一种Pythonic方式,用于声明我们有意丢弃列表理解的结果 . 这不是一个错误 .(基于dansalmo对_735878的回答,因为它似乎应该是它自己的答案 . )
如果您不介意使用外部库,我知道有两个本地实现此操作:
不确定这是否是一个好方法,但也可以这样做
例如,按偶数和奇数拆分列表
或者一般来说:
Advantages:
最短的无形方式
谓词仅对每个元素应用一次
Disadvantages