【问题标题】:Can a Python function take a generator and return generators to subsets of its generated output?Python 函数可以使用生成器并将生成器返回到其生成输出的子集吗?
【发布时间】:2009-08-01 00:24:45
【问题描述】:

假设我有一个像这样的生成器函数:

import random
def big_gen():
  i = 0
  group = 'a'
  while group != 'd':
    i += 1
    yield (group, i)
    if random.random() < 0.20:
      group = chr(ord(group) + 1)

示例输出可能是: ('a', 1), ('a', 2), ('a', 3), ('a', 4), ('a', 5), ('a', 6), (' a', 7), ('a', 8), ('b', 9), ('c', 10), ('c', 11), ('c', 12), ('c' , 13)

我想将其分为三组:A 组、B 组和 C 组。我希望每个组都有一个生成器。然后我将生成器和组字母传递给子函数。子函数的一个例子:

def printer(group_letter, generator):
  print "These numbers are in group %s:" % group_letter
  for num in generator:
    print "\t%s" % num

期望的输出是:

These numbers are in group a:
1
2
3
4
5
6
7
8
These numbers are in group b:
9
These numbers are in group c:
10
11
12
13

如何在不更改 big_gen() 或 printer() 的情况下做到这一点,并避免一次将整个组存储在内存中? (在现实生活中,这些群体庞大

【问题讨论】:

  • 现实生活中,大发电机多走几趟行不行?或者数据是否以某种不可撤销的方式被消耗?如果我正确理解了您的示例,则调用 random() 的状态不会存储在任何有用的地方,因此您无法再次使用该生成器。

标签: python generator


【解决方案1】:

当然,这就是你想要的:

import itertools
import operator

def main():
  for let, gen in itertools.groupby(big_gen(), key=operator.itemgetter(0)):
    secgen = itertools.imap(operator.itemgetter(1), gen)
    printer(let, secgen)

groupby 在这里完成了大部分工作——key= 只是告诉它要分组的字段。

生成的生成器需要包装在 imap 中,因为您已指定 printer 签名以在数字上采用迭代器,而本质上,groupby 在其获得的相同项目上返回迭代器它的输入——这里是 2 项元组,一个字母后跟一个数字——但这与你的问题的标题并没有那么密切的关系。

该标题的答案是,是的,Python 函数可以完美地完成您想要的工作——itertools.groupby 实际上正是这样做的。我建议仔细研究itertools 模块,它是一个非常有用的工具(并且提供了出色的性能)。

【讨论】:

    【解决方案2】:

    你这里有一个小问题。您希望 print() 的函数为每个组获取一个生成器,但实际上您拥有相同的生成器来生成所有组。在我看来,你有两个选择:

    1) 将 big_gen() 更改为 yield 生成器:

    import random
    def big_gen():
      i = 0
      group = 'a'
      while group != 'd':
        def gen():
            i += 1
            yield i
            if random.random() < 0.20:
                group = chr(ord(group) + 1)
        yield group, gen
    
     from itertools import imap
     imap(lambda a: printer(*a), big_gen())
    

    2) 更改 printer() 以保持状态并在组更改时通知(保留您原来的 big_gen() 函数):

    def printer(generator):
      group = None
      for grp, num in generator:
        if grp != group:
            print "These numbers are in group %s:" % grp
            group = grp
        print "\t%s" % num
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-10-02
      • 1970-01-01
      • 1970-01-01
      • 2016-12-16
      • 1970-01-01
      • 2023-03-24
      • 2020-04-23
      相关资源
      最近更新 更多