Python生成器表达式if-else

Python生成器表达式if-else,python,generator-expression,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)

我正在使用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)
def为低质量(读取):
lowqual\u bp=(如果bq允许的数量:
返回真值
其他:
返回错误
lowqual=(在SeqIO.parse(r_文件,“fastq”)中读取,如果是低质量(读取)==True)
highqual=(在SeqIO.parse(r_文件,“fastq”)中读取,如果是低质量(读取)==False)
顺序写入(高质量、flt\u out\u句柄、“fastq”)
SeqIO.write(低质量、垃圾处理、“快速处理”)
定义iter_长度(第二代):
返回和(1代表第i列)

您可以将
itertools.tee
itertools.ifilter
itertools.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(序列)
).

我认为您正在努力避免重复收集两次。如果是这样,这种方法是有效的:

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

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

这很难做到优雅。以下是一些有效的方法:

from itertools import tee, ifilter, ifilterfalse
low, high = [f(condition, g) for f, g in zip((ifilter, ifilterfalse), tee(seq))]
请注意,当您从一个结果迭代器(例如
)中消费项时,tee中的内部数据必须展开以包含您尚未从
中消费的任何项(不幸的是,包括那些
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())
这将从函数的代码域返回一个
dict
,并返回给生成器,生成器提供在
func
下获取该值的项:

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

请注意,您有责任确保
条件
只返回
codomain
中的值,这只是为了添加一个更一般的答案:如果您主要关心的是内存,您应该使用一个生成器循环整个文件,并尽可能低或高地处理每个项。比如:

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

如果您需要在使用任何一个元素之前收集所有高/低元素,那么您无法防止潜在的内存命中。原因是,在阅读之前,您无法知道哪些元素是高/低。如果您必须先处理low,而所有元素实际上都是high,那么您别无选择,只能在运行时将它们存储在一个列表中,这将使用内存。使用一个循环可以让您一次处理一个元素,但您必须平衡这一点与其他问题(即,以这种方式进行操作有多麻烦,这完全取决于您试图对数据执行的操作)。

作为旁注,不要将其与真/假进行比较。使用
if is\u condition\u true(r)
if not is\u condition\u true(r)
。delnan是对的,其他事情都可以。这看起来很好。这失败了吗?这就是你问的原因吗?它可能有效,但它既丑陋又低效。如果
sequences
是迭代器,它也会中断(不过你可以使用
itertools.tee
)。在你创建生成器之后,你如何使用
low
high
?这也会创建一个[None]*len(sequences)列表,这是不可取的,因为它使用了更多的内存作为他的原始建议。您可以使用
any(…)
和等效的生成器表达式,而不是使用列表理解。由于每个项都是
None
,因此为false,
any()
保证使用整个迭代器。(您也可以使用
collections.deque(maxlen=0)
来使用迭代器;它可能会更快,因为它不进行真实性测试。)我发现在涉及副作用(特别是追加)时使用for循环通常更简单(也更可读)。所以在这种情况下,我肯定会把它写成一个循环。我想避免列表理解,因为它们很昂贵。好的,让我看看any和deque-我对它们不熟悉他不希望生成列表来节省内存。哦,我必须避免高内存的情况,所以不能使用它。序列是迭代器,而不是生成器。sequences=SeqIO.parse(读取_文件,“fastq”)是否仍会中断?什么类型的迭代器?迭代器是Python中可以迭代的任何东西的通用术语。它来自Biopython包“…Bio.SeqIO.parse(),它接受文件句柄和格式名称,并返回SeqRecord迭代器“OK”。所以现在你的问题有道理了。您有一个包含许多记录的大文件,您希望根据一些过滤器将该文件拆分为两个较小的文件,每个文件包含文件的一半,而无需将整个内容读入内存。是这样吗?在这种情况下,最好的选择是在每个记录从输入迭代器中出来时,一次一个地将记录写入相应的文件。这占用了最少的内存,只迭代一次。很抱歉,我不明白。上面的机器是发电机吗?我想为每个数组使用一个生成器,因为我认为它实际上并没有存储在内存中,对吧,它只是一个表达式?我需要用Bio.SeqIO.write将数据打印出来——如果不在每次循环时调用它,效率会更高。或者,我可以使用简单的打印语句在每个步骤中打印。归根结底,制造发电机真的很贵吗?在哪种情况下,创建2更重要?@Nupur:以上只是一个循环。创建生成器的唯一原因是在循环中使用它。您目前似乎有一个生成器(称为
序列)<