Python生成器表达式if-else

时间:2012-08-24 16:22:19

标签: python generator-expression

我使用Python来解析一个大文件。我想做的是

If condition =True
   append to list A
else 
   append to list B

我想为此使用生成器表达式 - 以节省内存。我正在加入实际的代码。

def is_low_qual(read):
    lowqual_bp=(bq for bq in phred_quals(read) if bq < qual_threshold)  
    if iter_length(lowqual_bp) >  num_allowed:
        return True
    else:
        return False  

lowqual=(read for read in SeqIO.parse(r_file,"fastq") if is_low_qual(read)==True)
highqual=(read for read in SeqIO.parse(r_file,"fastq") if is_low_qual(read)==False)


SeqIO.write(highqual,flt_out_handle,"fastq")
SeqIO.write(lowqual,junk_out_handle,"fastq")

def iter_length(the_gen):
    return sum(1 for i in the_gen)

4 个答案:

答案 0 :(得分:6)

您可以将itertools.teeitertools.ifilteritertools.ifilterfalse结合使用:

import itertools
def is_condition_true(x):
    ...

gen1, gen2 = itertools.tee(sequences)
low = itertools.ifilter(is_condition_true, gen1)
high = itertools.ifilterfalse(is_condition_true, gen2)

使用tee可确保函数正常工作,即使序列本身就是一个生成器。

但请注意,tee本身可以使用相当大的内存(最多为大小len(sequences)的列表),如果lowhigh在不同时使用费率(例如,如果在使用low之前high已用尽)。

答案 1 :(得分:1)

只是添加一个更一般的答案:如果你主要关心的是内存,你应该使用一个生成器循环遍历整个文件,并处理每个项目的低或高。类似的东西:

for r in sequences:
    if condition_true(r):
        handle_low(r)
    else:
        handle_high(r)

如果您需要在使用之前收集所有高/低元素,那么您无法防止潜在的内存命中。原因是在阅读之前,您无法知道哪些元素是高/低。如果你必须首先处理低,并且事实证明所有元素实际上都很高,你别无选择,只能将它们存储在列表中,这将使用内存。使用一个循环执行此操作允许您一次处理一个元素,但是您必须与其他问题进行平衡(即,这样做有多麻烦,这将取决于您正在尝试做什么与数据)。

答案 2 :(得分:0)

我认为你正在努力避免两次迭代你的收藏。如果是这样,这种方法有效:

high, low = [], []
_Nones = [high.append(x) if is_condition_true() else low.append(x) for x in sequences]

这可能比建议少,因为它使用列表理解来产生副作用。这通常是反pythonic。

答案 3 :(得分:0)

这很难做到优雅。这是有用的:

from itertools import tee, ifilter, ifilterfalse
low, high = [f(condition, g) for f, g in zip((ifilter, ifilterfalse), tee(seq))]

请注意,当您从一个生成的迭代器(例如low)中使用项目时,tee中的内部双端队列必须展开以包含您尚未从high中消耗的任何项目(包括,不幸的是,ifilterfalse将拒绝的那些人。因此,这可能不会像您希望的那样节省大量内存。

这是一个使用尽可能少的额外内存的实现:

def filtertee(func, iterable, codomain=(False, True)):
    it = iter(iterable)
    deques = dict((r, deque()) for r in codomain)
    def gen(mydeque):
        while True:
            while not mydeque:          # as long as the local deque is empty
                newval = next(it)       # fetch a new value,
                result = func(newval)   # find its image under `func`,
                try:
                    d = deques[result]  # find the appropriate deque, and
                except KeyError:
                    raise ValueError("func returned value outside codomain")
                d.append(newval)        # add it.
            yield mydeque.popleft()
    return dict((r, gen(d)) for r, d in deques.items())

这会将函数的codomain中的dict返回给生成器,该生成器提供在func下获取该值的项:

gen = filtertee(condition, seq)
low, high = gen[True], gen[False]

请注意,您有责任确保condition仅返回codomain中的值。