Mam generator, generator
a także wygodną metodę - 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?
Czy generate_all
return
lub yield
? Chcę, aby użytkownicy obu metod używali tego samego, tj
for x in generate_all()
powinno być równe
some_list = get_the_list()
for x in generate(some_list)
Odpowiedzi:
Generatory tak leniwie oceniają
return
lubyield
zachowują się inaczej podczas debugowania kodu lub zgłaszania wyjątku.Z
return
każdym wyjątkiem, który zdarzy się w twoimgenerator
przypadku, nie będziesz o niczym wiedziałgenerate_all
, to dlatego, że kiedygenerator
naprawdę zostanie wykonany, opuściłeś już tęgenerate_all
funkcję. Zyield
tam będzie miałgenerate_all
ślad.A jeśli używa
yield from
:Jest to jednak kosztem wydajności. Dodatkowa warstwa generatora ma pewne narzuty.
return
Będzie więc ogólnie nieco szybszy niżyield from ...
(lubfor item in ...: yield item
). W większości przypadków nie będzie to miało większego znaczenia, ponieważ cokolwiek zrobisz w generatorze, zazwyczaj dominuje w czasie wykonywania, więc dodatkowa warstwa nie będzie zauważalna.yield
Ma jednak kilka dodatkowych zalet: nie jesteś ograniczony do jednej iteracji, możesz również z łatwością uzyskać dodatkowe przedmioty:W twoim przypadku operacje są dość proste i nie wiem, czy konieczne jest utworzenie wielu funkcji do tego celu, można łatwo użyć wbudowanego
map
lub wyrażenia generatora:Oba powinny być identyczne (z wyjątkiem pewnych różnic, gdy zdarzają się wyjątki) do użycia. A jeśli potrzebują bardziej opisowej nazwy, nadal możesz zawinąć je w jedną funkcję.
Istnieje wiele pomocników, które zawierają bardzo popularne operacje na wbudowanych iteracjach, a dalsze można znaleźć we wbudowanym
itertools
module. W takich prostych przypadkach po prostu uciekam się do nich i tylko w przypadku spraw innych niż trywialne napisz własne generatory.Ale zakładam, że twój prawdziwy kod jest bardziej skomplikowany, więc może nie mieć zastosowania, ale pomyślałem, że nie byłby to kompletna odpowiedź bez podania alternatyw.
źródło
Prawdopodobnie szukasz Delegacji Generatora (PEP380)
Jest dość zwięzły i ma wiele innych zalet, takich jak możliwość łączenia dowolnych / różnych iteracji!
źródło
list
? To zły przykład, nie wklejony w pytaniu prawdziwy kod, prawdopodobnie powinienem go edytować.yield from map(do_something, iterable)
a nawetyield from (do_something(x) for x in iterable)
yield from
ma sensu, chyba że twoje opakowanie zrobi coś innego generator-y.return generator(list)
robi co chcesz. Ale zauważ tobyłoby równoważne, ale z możliwością uzyskania większej wartości po
generator
wyczerpaniu. Na przykład:źródło
yield from
ireturn
kiedy konsument generatorathrows
jest w nim wyjątkiem - i innymi operacjami, na które ma wpływ ślad stosu.W tym konkretnym przypadku następujące dwie instrukcje wydają się funkcjonalnie równoważne:
i
Później jest mniej więcej taki sam jak
return
Wyrażenie zwraca generator, którego szukasz. Ayield from
lubyield
oświadczenie zamienia całą swoją funkcję w coś, co zwraca generator, który przechodzi przez jeden szukasz.Z punktu widzenia użytkownika nie ma różnicy. Jednak wewnętrznie
return
jest prawdopodobnie bardziej wydajny, ponieważ nie owija sięgenerator(list)
w zbędny generator pass-thru. Jeśli planujesz wykonać jakiekolwiek przetwarzanie elementów owiniętego generatora, użyjyield
oczywiście jakiejś formy .źródło
Ty byś
return
to zrobił .yield
ing * spowodowałobygenerate_all()
ocenę samego generatora i wywołanienext
tego zewnętrznego generatora zwróciłoby wewnętrzny generator zwrócony przez pierwszą funkcję, czego nie chciałbyś.*
Nie liczącyield from
źródło