Pode uma função Python tomar um gerador e geradores de retorno para subconjuntos de sua saída gerada?

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

  •  06-07-2019
  •  | 
  •  

Pergunta

Vamos dizer que eu tenho uma função de gerador como este:

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)

Exemplo de saída podem ser: ( 'Um', 1), ( 'a', 2), ( 'um', 3), ( 'um', 4), ( 'um', 5), ( 'um', 6), (' um', 7), ( 'um', 8), ( 'b', 9), ( 'c', 10), ( 'c', 11), ( 'c', 12), ( 'c' , 13)

Gostaria de quebrar este em três grupos: Grupo A, Grupo B e Grupo C. e eu gostaria de um gerador para cada grupo. Então eu passar o gerador e a letra do grupo em uma subfunção. Um exemplo da subfunção:

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

A saída desejada seria:

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

Como posso fazer isso sem alterar big_gen () ou impressora (), e evitar o armazenamento de todo o grupo na memória de uma vez? (Na vida real, os grupos são enorme )

Foi útil?

Solução

Claro, isso faz o que quiser:

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 faz a maior parte do trabalho aqui - o key= apenas diz que o campo para agrupar.

O gerador resultando precisa ser envolto em um imap só porque você tenha especificado sua assinatura printer tomar um iterador sobre número, enquanto que, por natureza, groupby retorna iteradores sobre os mesmos itens que ela recebe como sua entrada - aqui, 2-itens tuplas com uma letra seguida de um número -. mas isso não é realmente tudo o que pertinente para o título de sua pergunta

A resposta a esse título é que, sim, uma função Python pode perfeitamente fazer o trabalho que você quer - itertools.groupby na verdade faz exatamente isso. Eu recomendo estudar a itertools módulo com cuidado, é uma ferramenta muito útil (e proporciona excelente desempenho como bem).

Outras dicas

Você tem um pequeno problema aqui. Você gostaria que a função da impressora () para tomar um gerador para cada grupo, mas na realidade você tem o mesmo gerador produzindo todos os grupos. Você tem duas opções, como eu vê-lo:

1) Mudança big_gen () para produzir geradores:

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) Mudança da impressora () para manter o estado e notificação quando as alterações de grupo (mantendo o seu big_gen original () função):

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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top