Python将生成器分成两部分

sla*_*law 8 python generator python-3.x

我可以访问一个产生两个值的生成器:

def get_document_values():
    docs = query_database()  # returns a cursor to database documents
    for doc in docs:
        # doc is a dictionary with ,say, {'x': 1, 'y': 99}
        yield doc['x'], doc['y']
Run Code Online (Sandbox Code Playgroud)

我有另一个函数,process_x我无法更改,可以将生成器作为输入来处理x所有文档(如果生成元组,那么它只处理元组的第一个元素并忽略其他元素):

X = process_x(get_document_values())  # This processes x but ignores y
Run Code Online (Sandbox Code Playgroud)

但是,我还需要存储y生成器中的所有值.我唯一的解决方案是执行get_document_values两次:

Y = [y for x,y in get_document_values()]  #Throw away x
X = process_x(get_document_values())      #Throw away y
Run Code Online (Sandbox Code Playgroud)

这在技术上有效,但是当有许多文档要处理时,新文档可能会插入到数据库中,并且长度XY将会不同.需要在X和之间进行一对一的映射,Y并且我只需要调用get_document_values一次而不是两次.

我考虑过这样的事情:

Y = []

def process_y(doc_generator):
    global Y
    for x,y in doc_generator:
        Y.append(y)
        yield x

X = process_x(process_y(get_document_values()))
Run Code Online (Sandbox Code Playgroud)

但:

  1. 这感觉不到pythonic
  2. Y 需要声明为全局变量

我希望有更清洁,更pythonic的方式来做到这一点.

更新

实际上,get_document_values返回值x太大而无法集中存储到内存中,process_x实际上会降低内存需求.因此,无法缓存所有内容x.缓存所有这些y都很好.

Net*_*ave 2

调用时您已将所有值缓存到列表中:

all_values = [(x,y) for x,y in get_document_values()] #or list(get_document_values())
Run Code Online (Sandbox Code Playgroud)

您可以使用以下方法获取值的迭代器y

Y = map(itemgetter(1), all_values)
Run Code Online (Sandbox Code Playgroud)

为了x简单的使用:

X = process_x(map(itemgetter(0), all_values))
Run Code Online (Sandbox Code Playgroud)

另一种选择是分离生成器,例如:

def get_document_values(getter):
    docs = query_database()  # returns a cursor to database documents
    for doc in docs:
        # doc is a dictionary with ,say, {'x': 1, 'y': 99}
        yield getter(doc)

from operator import itemgetter
X = process_x(get_document_values(itemgetter("x")))
Y = list(get_document_values(itemgetter("y")))
Run Code Online (Sandbox Code Playgroud)

这样你就必须执行两次查询,如果你找到一种执行一次查询并复制游标的方法,那么你也可以将其抽象:

def get_document_values(cursor, getter):
    for doc in cursor:
        # doc is a dictionary with ,say, {'x': 1, 'y': 99}
        yield getter(doc)
Run Code Online (Sandbox Code Playgroud)

  • 又好又简单。但也许不使用“all”作为变量名。 (2认同)