【问题标题】:Nicest, efficient way to get result tuple of sequence items fulfilling and not fulfilling condition获得满足和不满足条件的序列项的结果元组的最好、最有效的方法
【发布时间】:2010-09-06 09:18:00
【问题描述】:

(这是专业的最佳实践/模式兴趣,不是家庭作业要求)

  • INPUT:任何无序序列或生成器items,如果满足过滤条件,函数myfilter(item)返回True p>

  • OUTPUT(filter_true, filter_false) 的序列元组 原始类型,其中包含 元素划分依据 按原始顺序过滤。

如果不进行双重过滤,您将如何表达这一点,或者我应该使用双重过滤?也许过滤器和循环/生成器/列表理解 next 可以回答?

我应该去掉保持类型的要求还是只是改变要求给出元组/生成器结果的元组,我不能轻易地返回生成器输入的生成器,或者我可以吗? (要求是自制的)

这里测试目前最好的候选人,提供两个流而不是元组

import itertools as it
from sympy.ntheory import isprime as myfilter

mylist = xrange(1000001,1010000,2)
left,right = it.tee((myfilter(x), x) for x in mylist)
filter_true = (x for p,x in left if p)
filter_false = (x for p,x in right if not p)

print 'Hundred primes and non-primes odd  numbers'
print  '\n'.join( " Prime %i, not prime %i" %
                  (next(filter_true),next(filter_false))
                  for i in range(100))

【问题讨论】:

    标签: python filter generator data-partitioning


    【解决方案1】:

    这是一种方法,它只为每个项目调用一次myfilter,如果mylist 是一个生成器,它也可以工作

    import itertools as it
    left,right = it.tee((myfilter(x), x) for x in mylist)
    filter_true = (x for p,x in left if p)
    filter_false = (x for p,x in right if not p)
    

    【讨论】:

    • 看起来不错!我的想法是从左右过滤到 (left, None) 或 (None, right) 并用 ifilter 从 Nones 中过滤掉左右。但你意识到只有一侧需要Noned。
    【解决方案2】:

    假设您的问题不是内存而是 cpu,myfilter 很重,并且您不想迭代和过滤原始数据集两次。以下是一些单程通行证的想法:

    简单而通用的版本(memoryvorous):

    filter_true=[]
    filter_false=[]
    for item in  items:
        if myfilter(item):
            filter_true.append(item)
        else:
            filter_false.append(item)  
    

    内存友好版本:(不适用于生成器(除非与列表(项目)一起使用))

    while items:
        item=items.pop()
        if myfilter(item):
            filter_true.append(item)
        else:
            filter_false.append(item)  
    

    生成器友好版本:

    while True:
        try:
            item=next(items)
            if myfilter(item):
                filter_true.append(item)
            else:
                filter_false.append(item)  
        except StopIteration:
            break
    

    【讨论】:

    • 如果可以将所有数据存储为列表,则效果很好,但有时生成器会更好。
    • 输入可以是例如带有 6 GB 备份文件的 tarfile.open('backup.tar.bz2', 'r:bz2')。我只想要任何一种类型的输入中的一个项目,比如 jpg 文件和其他文件。
    • 您的生成器仍然需要运行直到结束并通过附加生成列表,因此它是列表版本。通过 pop 取值有时可以替代生成器,我自己曾经发过一篇这样的帖子。但是 filter_true.pop(0) 或 filter_false.pop(0), pop() 从列表的末尾弹出,你搞乱了序列的顺序。
    【解决方案3】:

    简单的方法(但效率较低)是 tee 可迭代并过滤它们:

    import itertools
    left, right = itertools.tee( mylist )
    filter_true = (x for x in left if myfilter(x))
    filter_false = (x for x in right if myfilter(x))
    

    这比最优解决方案效率低,因为myfilter 会为每个元素重复调用。也就是说,如果您已经在left 中测试过一个元素,那么您不必在right 中重新测试它,因为您已经知道答案了。如果您需要这种优化,实现起来应该不难:查看tee 的实现以获取线索。对于每个返回的可迭代对象,您都需要一个双端队列,其中包含应该放入其中但尚未被请求的原始序列的元素。

    【讨论】:

      【解决方案4】:

      我认为你最好的选择是构建两个独立的生成器:

      filter_true = (x for x in mylist if myfilter(x))
      filter_false = (x for x in mylist if not myfilter(x))
      

      【讨论】:

      • 不幸的是,这仅适用于列表,而不适用于生成器——您没有两次机会遍历生成器。
      猜你喜欢
      • 2020-01-22
      • 1970-01-01
      • 2018-07-21
      • 2019-12-11
      • 1970-01-01
      • 2015-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多