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

StackOverflow https://stackoverflow.com/questions/1215464

  •  06-07-2019
  •  | 
  •  

Вопрос

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

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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top