Может ли функция Python взять генератор и вернуть генераторы в подмножества своего сгенерированного вывода?
Вопрос
Допустим, у меня есть такая функция генератора:
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 (), и избегая одновременного сохранения всей группы в памяти? (В реальной жизни группы огромны )
Решение
Конечно, это делает то, что вы хотите:
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 , это очень полезный инструмент (и обеспечивает великолепную производительность, так как а). р>
Другие советы
У вас есть небольшая проблема здесь. Вы бы хотели, чтобы функция 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