Eu tenho um gerador generator
e também um método de conveniência para ele - generate_all
.
def generator(some_list):
for i in some_list:
yield do_something(i)
def generate_all():
some_list = get_the_list()
return generator(some_list) # <-- Is this supposed to be return or yield?
Deveria generate_all
return
ou yield
? Eu quero que os usuários dos dois métodos usem o mesmo, ou seja,
for x in generate_all()
deve ser igual a
some_list = get_the_list()
for x in generate(some_list)
Respostas:
Os geradores estão avaliando preguiçosamente
return
ouyield
se comportarão de maneira diferente quando você estiver depurando seu código ou se uma exceção for lançada.Com
return
qualquer exceção que vocêgenerator
não saibagenerate_all
, é porque quandogenerator
é realmente executado você já deixou agenerate_all
função. Comyield
lá vai tergenerate_all
no traceback.E se estiver usando
yield from
:No entanto, isso tem um custo de desempenho. A camada adicional do gerador possui alguma sobrecarga. Então
return
, geralmente será um pouco mais rápido queyield from ...
(oufor item in ...: yield item
). Na maioria dos casos, isso não importa muito, porque o que você faz no gerador normalmente domina o tempo de execução, para que a camada adicional não seja perceptível.No entanto,
yield
possui algumas vantagens adicionais: você não está restrito a uma única iterável, mas também pode gerar itens adicionais com facilidade:No seu caso, as operações são bastante simples e não sei se é necessário criar várias funções para isso, seria possível usar facilmente a
map
expressão incorporada ou de gerador:Ambos devem ser idênticos (exceto por algumas diferenças quando ocorrerem exceções) a serem usados. E se eles precisarem de um nome mais descritivo, você ainda poderá agrupá-los em uma função.
Existem vários auxiliares que envolvem operações muito comuns nos iterables internos e outros podem ser encontrados no
itertools
módulo interno. Em casos tão simples, eu simplesmente recorreria a esses e somente para casos não triviais escreva seus próprios geradores.Mas suponho que seu código real seja mais complicado, pelo que pode não ser aplicável, mas achei que não seria uma resposta completa sem mencionar alternativas.
fonte
Você provavelmente está procurando Delegação de Gerador (PEP380)
É bastante conciso e também tem várias outras vantagens, como poder encadear iteráveis arbitrários / diferentes!
fonte
list
? É um mau exemplo, não um código real colado na pergunta, eu provavelmente deveria editá-lo.yield from map(do_something, iterable)
ou até mesmoyield from (do_something(x) for x in iterable)
yield from
é inútil, a menos que seu invólucro faça algo mais gerador-y.return generator(list)
faz o que você quer. Mas note queseria equivalente, mas com a oportunidade de gerar mais valores após o
generator
esgotamento. Por exemplo:fonte
yield from
ereturn
quando o consumidor do gerador éthrows
uma exceção dentro dele - e com outras operações que são influenciadas pelo rastreamento da pilha.As duas instruções a seguir parecerão funcionalmente equivalentes nesse caso específico:
e
O posterior é aproximadamente o mesmo que
A
return
instrução retorna o gerador que você está procurando. Uma instruçãoyield from
ouyield
transforma toda a sua função em algo que retorna um gerador, que passa pelo que você está procurando.Do ponto de vista do usuário, não há diferença. Internamente, no entanto,
return
é indiscutivelmente mais eficiente, pois não envolvegenerator(list)
um gerador de passagem supérfluo. Se você planeja fazer qualquer processamento nos elementos do gerador empacotado, use alguma forma, éyield
claro.fonte
Você faria
return
isso.yield
ing * causariagenerate_all()
a avaliação de um gerador em si, e chamarnext
esse gerador externo retornaria o gerador interno retornado pela primeira função, que não é o que você deseja.*
Não incluindoyield from
fonte