Может ли функция Python взять генератор и вернуть генераторы в подмножества своего сгенерированного вывода? - PullRequest
5 голосов
/ 01 августа 2009

Допустим, у меня есть функция генератора, подобная этой:

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)

Пример вывода может быть: («а», 1), («а», 2), («а», 3), («а», 4), («а», 5), («а», 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 (), и избегая одновременного сохранения всей группы в памяти? (В реальной жизни группы огромны )

Ответы [ 2 ]

8 голосов
/ 01 августа 2009

Конечно, это делает то, что вы хотите:

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 , это очень полезный инструмент (а также обеспечивает великолепную производительность).

0 голосов
/ 01 августа 2009

У вас небольшая проблема здесь. Вы бы хотели, чтобы функция printer () принимала генератор для каждой группы, но в действительности у вас есть один и тот же генератор, выдающий все группы. У меня есть два варианта, на мой взгляд:

1) Измените big_gen (), чтобы получить генераторы:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...