2017-01-29 59 views
3

生成生成聚合结果的生成器的Pythonic方法是什么?在元的代码,这样的事情(而不是真实的,因为我的Python版本不支持混合的产量和返回):生成生成器和聚合结果的Python函数

def produce(): 
    total = 0 
    for item in find_all(): 
     total += 1 
     yield item 

    return total 

在我看来,我可以:

  1. 不作produce()一个发电机,但传递一个回调函数来调用每个item
  2. 对于每个yield,还有yield到目前为止的总计结果。我宁愿不计算每个产量的中间结果,只有在完成时。
  3. 发送一个dict作为produce()的参数,该参数将填充聚合结果。
  4. 使用全局来存储聚合结果。

所有这些似乎没有太大的吸引力...

NB。 total是一个简单的例子,我的实际代码需要复杂的聚合。我需要produce()完成之前的中间结果,因此是一个发电机。

+1

所以总同样是迭代的长度的仅仅是等效由'find_all'返回?为此你可以使用'enumerate'。 –

回答

3

也许你不应该使用一个生成器,而是一个迭代器。

def findall(): # no idea what your "find_all" does so I use this instead. :-) 
    yield 1 
    yield 2 
    yield 3 

class Produce(object): 
    def __init__(self, iterable): 
     self._it = iterable 
     self.total = 0 

    def __iter__(self): 
     return self 

    def __next__(self): 
     self.total += 1 
     return next(self._it) 

    next = __next__ # only necessary for python2 compatibility 

也许更清楚地看这一个例子:

>>> it = Produce(findall()) 
>>> it.total 
0 
>>> next(it) 
1 
>>> next(it) 
2 
>>> it.total 
2 
0

我错过了什么吗?为什么不:

def produce(): 
    total = 0 
    for item in find_all(): 
     total += 1 
     yield item 

    yield total 
+0

谢谢,我没有想到这个!它可以工作,但调用者必须弄清楚它是否有'item'对象或'total'对象。嗯。 – Willem

1

可以使用enumerate来算的东西,例如

i=0 
for i,v in enumerate(range(10), 1): 
    print(v) 
print("total",i) 

(注意枚举的初始值)

对于更复杂的东西,你可以使用相同的原理,制作一个generato r产生两个值并在迭代中忽略一个值,并在完成后使用它。

其它替代方案是在任一情况下经过一个可修改的对象,例如

def produce(mem): 
    t=0 
    for x in range(10): 
     t+=1 
     yield x 
    mem.append(t) 

aggregate=[] 
for x in produce(aggregate): 
    print(x) 
print("total",aggregate[0]) 

结果对于该实施例中

0 
1 
2 
3 
4 
5 
6 
7 
8 
9 
total 10